master #16

Closed
LeoMoulin wants to merge 19 commits from master into salutcesmoileo
30 changed files with 1764 additions and 61 deletions

77
.drone.yml Normal file
View File

@ -0,0 +1,77 @@
---
kind: pipeline
type: docker
name: test
steps:
- name: Backend-Tests
image: eclipse-temurin:21
workspace: backend/
commands:
- echo "Backend CI Test TODO"
- name: FrontEnd-Tests
image: node:lts-alpine
workspace: frontend/
commands:
- echo "Frontend CI Test TODO"
---
kind: pipeline
type: docker
name: build
depends_on:
- test
steps:
- name: Backend-build
image: eclipse-temurin:21
workspace: backend/
commands:
- echo "Backend CI build TODO"
- name: FrontEnd-build
image: node:lts-alpine
workspace: frontend/
commands:
- echo "Frontend CI build TODO"
---
kind: pipeline
type: exec
name: deploy
depends_on:
- build
server:
host:
from_secret: host
user:
from_secret: user
ssh_key:
from_secret: ssh_key
plateform:
os: linux
arch: amd64
trigger:
branch:
- master
event:
- push
steps:
- name: deploy frontend
commands:
- cd frontend
- npm run build
- install -d "dist/" "$HOME/dist"
- name: deploy backend
commands:
- echo "deploying backend TODO"
---
kind: signature
hmac: 3c5387c57626f0946636d82b97fad0daaf09dbb5f02f201791363ff33cc0bb82
...

View File

@ -18,6 +18,10 @@ tags:
description: Scientifics articles extension's endpoints
- name: Ext (Schedule)
description: Schedule extension's endpoints
- name: Ext (Student Registration)
description: Student Registration's endpoints
- name: Ext (Messaging)
description: Messaging extension's endpoints
paths:
/ping:
@ -31,6 +35,662 @@ paths:
schema:
type: string
example: pong
# Messaging
/forum:
get:
summary: get list of available forum
security:
- bearer: []
tags:
- Ext (Messaging)
- Forum
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
type: object
required:
- id
- name
- owner
properties:
id:
type: integer
name:
type: string
owner:
type: integer
description: user id of the teacher
'401':
$ref: '#/components/responses/UnauthorizedError'
post:
summary: Create a new forum
security:
- bearer: []
tags:
- Ext (Messaging)
- Forum
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
name:
type: string
courseId:
type: integer
responses:
'201':
description: User created
'401':
$ref: '#/components/responses/UnauthorizedError'
/forum/{forumId}:
parameters:
- name: forumId
in: path
description: Id of the forum
required: true
schema:
type: integer
get:
summary: get informations about a forum
tags:
- Ext (Messaging)
- Forum
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
type: object
required:
- id
- name
- owner
properties:
id:
type: integer
name:
type: string
owner:
type: integer
description: user id of the teacher
topics:
type: array
items:
type: object
properties:
id:
type: integer
name:
type: string
autor:
type: integer
description: user Id
registered:
description: List of registered user only if have the authorisation to view
type: array
items:
type: integer
description: userId
'401':
$ref: '#/components/responses/UnauthorizedError'
delete:
summary: remove the forum
security:
- bearer: []
tags:
- Ext (Messaging)
- Forum
responses:
'201':
description: Forum deleted
'401':
$ref: '#/components/responses/UnauthorizedError'
/forum/{forumId}/topic:
parameters:
- name: forumId
in: path
description: Id of the forum
required: true
schema:
type: integer
get:
summary: list topics of a forum
tags:
- Ext (Messaging)
- Forum
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
type: object
required:
- id
- name
- author
properties:
id:
type: integer
name:
type: string
author:
type: integer
description: user id of the author
'401':
$ref: '#/components/responses/UnauthorizedError'
post:
summary: create a new topic in the forum
tags:
- Ext (Messaging)
- Forum
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
subject:
type: string
content:
type: integer
responses:
'201':
description: post created
'401':
$ref: '#/components/responses/UnauthorizedError'
/forum/{forumId}/topic/{topicId}:
parameters:
- name: forumId
in: path
description: Id of the forum
required: true
schema:
type: integer
- name: topicId
in: path
description: Id of the topic
required: true
schema:
type: integer
get:
summary: get info about a topic
tags:
- Ext (Messaging)
- Forum
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
required:
- id
- name
- author
- locked
properties:
id:
type: integer
name:
type: string
author:
type: integer
description: user id of the author
locked:
type: boolean
description: define if the topic is locked to new responses or not
'401':
$ref: '#/components/responses/UnauthorizedError'
delete:
summary: delete a topic
security:
- bearer: []
tags:
- Ext (Messaging)
- Forum
responses:
'201':
description: Topic deleted
'401':
$ref: '#/components/responses/UnauthorizedError'
/forum/{forumId}/topic/{topicId}/response:
parameters:
- name: forumId
in: path
description: Id of the forum
required: true
schema:
type: integer
- name: topicId
in: path
description: Id of the topic
required: true
schema:
type: integer
get:
summary: list responses of a topic
tags:
- Ext (Messaging)
- Forum
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
author:
type: integer
description: user id of the author
content:
type: string
'401':
$ref: '#/components/responses/UnauthorizedError'
post:
summary: create a new response
tags:
- Ext (Messaging)
- Forum
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
content:
type: integer
responses:
'201':
description: Message posted
'401':
$ref: '#/components/responses/UnauthorizedError'
/forum/{forumId}/topic/{topicId}/response/{responseId}:
parameters:
- name: forumId
in: path
description: Id of the forum
required: true
schema:
type: integer
- name: topicId
in: path
description: Id of the topic
required: true
schema:
type: integer
- name: responseId
in: path
description: Id of the response
required: true
schema:
type: integer
get:
summary: get info on a response
tags:
- Ext (Messaging)
- Forum
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
required:
- id
- author
- content
properties:
id:
type: integer
author:
type: string
content:
type: integer
'401':
$ref: '#/components/responses/UnauthorizedError'
delete:
summary: delete a response
tags:
- Ext (Messaging)
- Forum
responses:
'201':
description: Message deleted
'401':
$ref: '#/components/responses/UnauthorizedError'
/discussion:
get:
summary: get list of available discussions
tags:
- Ext (Messaging)
- discussion
responses:
'201':
description: OK
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
name:
type: string
users:
type: array
items:
type: integer
'401':
$ref: '#/components/responses/UnauthorizedError'
post:
summary: create a new discussion
tags:
- Ext (Messaging)
- discussion
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
name:
type: string
users:
type: array
description: id of users
items:
type: integer
responses:
'201':
description: Discussion created
'401':
$ref: '#/components/responses/UnauthorizedError'
/discussion/{id}:
parameters:
- name: id
in: path
description: Id of the discussion
required: true
schema:
type: integer
get:
summary: get info on a discussion
tags:
- Ext (Messaging)
- discussion
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
required:
- id
- name
- users
properties:
id:
type: integer
name:
type: string
users:
type: integer
description: user ids of participants
'401':
$ref: '#/components/responses/UnauthorizedError'
/discussion/{id}/msg:
parameters:
- name: id
in: path
description: Id of the discussion
required: true
schema:
type: integer
get:
summary: list messages in a discussion
tags:
- Ext (Messaging)
- discussion
responses:
'200':
description: Ok
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
datetime:
type: integer
author:
type: integer
content:
type: string
'401':
$ref: '#/components/responses/UnauthorizedError'
post:
summary: post a new message in a discussion
tags:
- Ext (Messaging)
- discussion
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: string
description: message content
responses:
'201':
description: Message sent
'401':
$ref: '#/components/responses/UnauthorizedError'
/discussion/{id}/msg/{msgId}:
parameters:
- name: id
in: path
description: Id of the discussion
required: true
schema:
type: integer
- name: msgId
in: path
description: Id of the message
required: true
schema:
type: integer
get:
summary: get info about message
tags:
- Ext (Messaging)
- discussion
responses:
'200':
description: Ok
content:
application/json:
schema:
type: object
properties:
id:
type: integer
datetime:
type: integer
author:
type: integer
content:
type: string
'401':
$ref: '#/components/responses/UnauthorizedError'
delete:
summary: delete a message
security:
- bearer: []
tags:
- Ext (Messaging)
- discussion
responses:
'201':
description: Message deleted
'401':
$ref: '#/components/responses/UnauthorizedError'
/appointment:
get:
summary: list appointments
tags:
- Ext (Messaging)
- appointment
parameters:
- name: type
in: query
required: false
schema:
type: string
enum: [open, closed]
responses:
'200':
description: Ok
content:
text/calendar:
schema:
type: string
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
teacher:
type: integer
student:
type: integer
date:
type: integer
status:
type: string
'401':
$ref: '#/components/responses/UnauthorizedError'
post:
summary: create a new appointment
tags:
- Ext (Messaging)
- appointment
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
content:
type: integer
datetime:
type: integer
to:
type: integer
description: user id of the person to request to
responses:
'201':
description: Appointment created
'401':
$ref: '#/components/responses/UnauthorizedError'
/appointment/{id}:
parameters:
- name: id
in: path
description: Id of the appointment
required: true
schema:
type: integer
get:
summary: get info on an appointment
tags:
- Ext (Messaging)
- appointment
responses:
'200':
description: Ok
content:
text/calendar:
schema:
type: string
application/json:
schema:
type: object
properties:
id:
type: integer
teacher:
type: integer
student:
type: integer
date:
type: integer
status:
type: string
'401':
$ref: '#/components/responses/UnauthorizedError'
post:
summary: Accept, decline or propose a new schedule for the appointment
tags:
- Ext (Messaging)
- appointment
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
action:
type: string
enum: [accept, refuse, propose]
data:
type: object
description: context dependant response (can be null)
responses:
'201':
description: Done
'401':
$ref: '#/components/responses/UnauthorizedError'
# general
/users:
get:
summary: list all users
@ -44,7 +704,7 @@ paths:
name: type
required: false
schema:
$ref: '#components/schemas/Roles'
$ref: '#/components/schemas/Roles'
responses:
'200':
description: OK
@ -377,7 +1037,141 @@ paths:
description: Cursus modified
'401':
$ref: '#/components/responses/UnauthorizedError'
/Requests/{type}:
get:
summary: Give an array of every requests of the specified type
tags :
- Requests
- SI
- Ext (Student Registration)
responses :
'200':
description : OK
content :
application/json :
schema :
type : array
items :
allOf:
- type : object
properties :
id :
type : integer
- oneOf :
- $ref : '#/components/schemas/RRRequest'
- $ref : '#/components/schemas/SSRequest'
- $ref : '#/components/schemas/UnRegRequest'
- $ref : '#/components/schemas/ExempRequest'
- $ref : '#/components/schemas/RegRequest'
discriminator:
propertyName : type
parameters:
- name: type
in: path
description: type of the request
required: true
schema:
type: integer
post :
summary: create a request
tags :
- Requests
- Guest
- Student
- Ext (Student Registration)
security:
- bearer: []
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
oneOf:
- $ref : '#/components/schemas/RRRequest'
- $ref : '#/components/schemas/SSRequest'
- $ref : '#/components/schemas/UnRegRequest'
- $ref : '#/components/schemas/ExempRequest'
- $ref : '#/components/schemas/RegRequest'
discriminator:
propertyName : type
responses:
'201':
description: New request created
'401':
$ref : '#/components/responses/UnauthorizedError'
/Requests/{type}/{id}:
parameters:
- name: type
in: path
description: type of the request
required: true
schema:
type: integer
- name : id
in : path
description: id of the selected Request
required : true
schema :
type : integer
get :
summary : Get all the data composing a request
tags :
- Requests
- SI
- Teacher
- Ext (Student Registration)
responses :
'200':
description : OK
content :
application/json :
schema :
allOf:
- type : object
properties :
id :
type : integer
- oneOf :
- $ref : '#/components/schemas/RRRequest'
- $ref : '#/components/schemas/SSRequest'
- $ref : '#/components/schemas/UnRegRequest'
- $ref : '#/components/schemas/ExempRequest'
- $ref : '#/components/schemas/RegRequest'
discriminator:
propertyName : type
patch:
summary: Change the state of request
tags:
- Requests
- SI
- Teacher
- Ext (Student Registration)
security:
- bearer: []
requestBody:
required : true
content:
application/x-www-form-urlencoded:
schema:
oneOf :
- $ref : '#/components/schemas/RRRequest'
- $ref : '#/components/schemas/SSRequest'
- $ref : '#/components/schemas/UnRegRequest'
- $ref : '#/components/schemas/ExempRequest'
- $ref : '#/components/schemas/RegRequest'
discriminator:
propertyName : type
responses:
'201':
description: State modified
'401':
$ref: '#/components/responses/UnauthorizedError'
/lesson:
post:
@ -777,6 +1571,7 @@ paths:
summary: get a list of article's data
tags:
- Users
- Guest
- Ext (scientific articles)
responses:
'200':
@ -886,6 +1681,84 @@ components:
Roles:
type: string
enum: [teacher, student, secretary]
RegRequest:
type : object
properties:
Name :
type: string
Firstname :
type : string
Address :
$ref: "#/components/schemas/Address"
Email:
type : string
BirthDate:
type : string
description: Follow the iso 8601 ("YYYY-MM-DD")
Cursus:
type : integer
description : ID of a specific cursus
Photo:
type : string
description: Path of the picture that the user uploaded
IdentityCard:
type : string
description: Path of the picture of the user's identity card
Degree:
type : string
description: Path of the pdf containing the user's degree
Certificate:
type : string
description: Path of the pdf containing the user's admission certificate (optionnal)
ForeignerCertificate:
type : string
description: Path of the pdf containing the user's foreigner certificate (optionnal)
State:
type : string
RRRequest:
type : object
properties :
RegNo :
type : integer
NewCursusid :
type : integer
State :
type : string
SSRequest:
type : object
properties :
RegNo :
type : integer
Amount :
type : integer
Document :
type : string
description : justification document for a scholarship
UnRegRequest :
type : object
properties :
RegNo :
type : integer
State :
type : string
ExempRequest :
type : object
properties :
RegNo :
type : integer
Courseid :
type : integer
State :
type : string
Article:
type: object
properties:
@ -1018,10 +1891,7 @@ components:
"displayMode": "grid",
"UserId": 12
}
responses:
UnauthorizedError:
description: Unauthorized access or missing bearer

View File

@ -0,0 +1,87 @@
# Entity relational diagram
## Messaging extension
```mermaid
%%{init: { "er": {"fontSize": 25, "stroke": "black" }}}%%
erDiagram
%% General
Users
%% Messages
Discussions{
Integer id PK
String name
}
Messages{
Integer id PK
Integer response FK "Messages"
String content
}
Discussions ||--o{ Messages: ""
Discussions ||--o{ Users: ""
Messages o|--o{ Messages: "answers"
%% Forums
Forums{
Integer id PK
String name
Integer course FK "Course"
}
Topics{
Integer id PK
String subject
String content
Boolean locked
}
Answers{
Integer id PK
String content
Boolean anonymous
TimeStamp creation_time
}
Polls{
Integer id PK
enum PollType
}
Options{
Integer id PK
String name
}
Forums ||--o{ Users: "Registered"
Forums ||--|| Teacher: "Owner"
Forums ||--o{ Topics: ""
Forums ||--o{ Polls: ""
Topics ||--|| Teacher: "Author"
Topics ||--|| Users: "Author"
Topics ||--o{ Answers: ""
Polls ||--o{ Options: ""
Options ||--o{ Votes : ""
Votes }o--|| Users: "Voter"
%% Appointments
Teacher
Appointments{
Integer id PK
Integer teacher FK "Teacher"
TIME sent_time
enum Status
}
Appointments ||--|| Users: ""
```
Debucquoy Anthony

View File

@ -11,8 +11,9 @@ extension_messagerie.pdf:use_case_messagerie.tex extension_messagerie.bbl extens
use_case_messagerie.tex: use_case_messagerie.uml
plantuml -tlatex:nopreamble use_case_messagerie.uml
image: use_case_messagerie.uml interaction_diagram.uml
image: use_case_messagerie.uml interaction_diagram.uml class.uml
plantuml $^
mmdc -i ERD.md -o ERD.png
extension_messagerie.bbl: extension_messagerie.bcf
biber extension_messagerie
@ -24,6 +25,7 @@ clean:
latexmk -C
rm -f use_case_messagerie.tex
rm -f extension_messagerie.{bbl,run.xml}
rm -f class.tex
run: extension_messagerie.pdf
xdg-open $<

View File

@ -0,0 +1,86 @@
@startuml
title Class diagram for Messaging extension
package Messages {
class Message{
content: String
response: Message
respond(User, String)
}
class Discussion{
name: String
users: ArrayList<User>
invite(User)
sendMessage(User, String)
}
Discussion *-- Message
}
package Forums {
class Forum{
name: String
Owner: Teacher
Registered: ArrayList<User>
{static} createForum(Course): Forum
createTopic(String): Topic
createPoll(String, PollType, ArrayList<Option>): Poll
}
class Topic{
subject: String
author: Teacher
content: String
answer(User, String): Answer
lock(Boolean)
}
class Answer{
author: User
content: String
anonymous: Boolean
remove()
}
class Poll{
options: ArrayList<Option>
type: PollType
answer(User, Option): Vote
addOption(Option)
}
class Option{
name: String
}
class Vote{
voter: User
}
enum PollType {
ALLOW_NEW_OPTIONS
ALLOW_MULTIPLE_CHOICE
}
note "Change the behaviour of poll" as N
Forum *-l- Topic
Topic *-l- Answer
Topic <|-d- Poll
Poll *-l- Option
Option "1..*" -d-x Vote
Poll *-- Vote
Poll -- PollType
PollType .r. N
}
package Appointments{
class Appointment{
date: Date
teacher: Teacher
student: Student
{static} Appointment(Student, Teacher, Date)
accept()
refuse()
propose(Date)
export(): File
}
}
Appointments -[hidden]d- Messages
@enduml

View File

@ -3,6 +3,7 @@
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{amsmath, amsfonts, amssymb, amsthm}
\usepackage{dirtytalk}
\usepackage{tikz}
\usepackage{biblatex}
@ -20,77 +21,74 @@
\resizebox{345pt}{!}{
\input{./use_case_messagerie.tex}
}
\caption{Use Case Diagram of the messaging extension}
\caption{Use Case Diagram pour l'extension de messagerie}
\label{fig:useCase:msg_ext}
\end{figure}
In the diagram Figure~\ref{fig:useCase:msg_ext},
the <<extend>> definition is not pulled from the "Genie logiciel" course
of the University of Mons (UMONS). It is in fact the definition from
the Dan Pilone's pocket reference \cite{Pilone2006-fn}.
Dans le diagramme \ref{fig:useCase:msg_ext}, la définition du <<extend>> est tirée du livre:
"uml Pocket reference" de Dan Pilone \cite{Pilone2006-fn}.
\say{Use case extension encapsulates a distinct flow of events
that are not considered part of the normal or basic flow.
They are not necessarily exceptional conditions.}
\subsection{Answer topic}
The user should be able to answer to a topic created by a teacher.
This answer will depend on the type of topic. The answer could be a selection on a poll,
a private answer (which mean that the answer is not visible to the other users except to the professor).
The answer could finally be a basic answer to the topic and be visible to other users browsing the topic.
L'utilisateur doit être capable de répondre à un topic crée par l'enseignant.
Cette réponse va dépendre du type de topic. Elle peut être une réponse à un questionnaire,
un message privé (qui ne serait pas visible par les autres utilisateur excepté l'enseignant) ou
une simple réponse qui serait visible par tous.
\subsection{Create discutions}
The user can select multiple users and group them into a discussion.
In this discussion, users will be able to exchange messages readable by
every other members of the discussion
L'utilisateur peut séléctionner plusieurs utilisateurs et les include dans un groupe de discussion.
Dans cette discussion, les utilisateurs peuvent échanger des messages lisible par les autres membres du
groupe
\subsubsection{Send messages}
Allow the creation of a message in a discussion.
The text sent to others shall be received by other users
and these users shall be notified of this message by the notification
system.
Permet la création d'un message dans une discusison.
Ce message va être reçu par les autres utilisateur et ceux-ci vont être notifié
du message par le système de notification
\subsection{Ask appointment}
A student can ask for an appointment to a teacher.
In this request, the student will have to give a date, a time
and a subject he want to propose to the teacher.
Un étudiant peut demander un rendes-vous à un enseignant.
Dans cette demande, l'étudiant doit donner une date qui lui convient pour ce rendez-vous
ainsi qu'une raison à ce rendez-vous.
\subsubsection{Export to calendar}
When an appointment is made. The program can export the event
to an open format that can be read by calendar software to add
the event to the user's calendar.
Quand un rendez-vous est crée, Le programme peut exporter l'événement dans un
format libre qui pourra être lu par les programme de gestions de planning.
\subsection{Manage appointment}
When a teacher received an appointment, he is able to
validate, deny or propose a new appointment schedule to the
student.
Quand un enseignant reçois une demande de rendez-vous,
il a la possibilité de valider/refuser ou de proposer un nouveau rendez-vous
à l'étudiant
\subsubsection{Propose new appointment}
If the teacher can't attend to an appointment because of his
schedule, he can make a new time proposal and send it to
the student. The student then receive a notification of
the proposed modifications.
Si l'enseignant n'est pas disponible pour le rendez-vous proposé,
il peut alors proposer un nouveau créneau horaire et l'envoyer à l'étudiant.
L'étudiant reçois alors une notification du nouveau créneau horaire.
\subsection{Create forum}
The teacher can create a new forum under a specific course
The new forum will then make every student of this course
follow the forum and its topic.
L'enseignant peut créer un nouveau forum lié à un cours.
Ce nouveau forum enregistrera les étudiant du cours automatiquement.
\subsubsection{Post topics}
The teacher can post a new topic inside a forum to let
the student know of something specific or to ask a question to
them.
L'enseignant peut envoyer un topic dans un forum pour préciser quelque chose aux étudiants
ou pour poser une questions
\subsubsection{Post poll}
When posting a topic to a forum, the teacher can choose to
make the post as a poll. In that case, student will have to vote
for one of the options or if allowed by the teacher, create a new option.
Lors d'un post, il est possible de choisir de créer un questionnaire à choix multiples ou non.
Dans ce cas, les étudiants vont devoir voter pour l'une des options proposée ou potentiellement
crée une nouvelle option.
\printbibliography

View File

@ -1,23 +1,19 @@
@startuml
note
Appointment
endnote
mainframe Appointment
start
:Asking Apointment]
:Asking Appointment]
repeat
if (accepted) is (yes) then
:Export to calendar]
stop
else (no)
:Propose new apointment]
:Propose new appointment]
endif
@enduml
@startuml
note
Messaging
endnote
mainframe Messaging
start
split
:Create Discution]
@ -31,10 +27,7 @@ stop
@enduml
@startuml
note
Forum
endnote
mainframe Forum
start
:Forum Creation]

View File

@ -5,14 +5,13 @@ left to right direction
:Student: as s
:Teacher: as t
package "Messagerie"{
(Answer topics) as at
(Ask appointement) as aa
(Create Discution) as cd
(Ask appointment) as aa
(Create Discussion) as cd
(Create Forum) as cf
(Manage appointement) as ma
(Manage appointment) as ma
s -- at
s -- aa
@ -32,7 +31,7 @@ cf <-- pt : << include >>
pt <|-- (Post poll)
cd <-- (Send messages) : << include >>
ma <-- (Propose new appointment) : << exlude>> \n [refuse]
ma <-- (Propose new appointment) : << extends >> \n [refuse]
}

30
frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
# Personal but I don't like to store the lock
package-lock.json

33
frontend/README.md Normal file
View File

@ -0,0 +1,33 @@
# Clyde
This is the frontend part of the Clyde application
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Compile and Minify for Production
```sh
npm run build
```
### Run Unit Tests with [Vitest](https://vitest.dev/)
```sh
npm run test:unit
```
### Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
```

13
frontend/index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

8
frontend/jsconfig.json Normal file
View File

@ -0,0 +1,8 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}

25
frontend/package.json Normal file
View File

@ -0,0 +1,25 @@
{
"name": "clyde",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test:unit": "vitest",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
},
"dependencies": {
"vue": "^3.4.15"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.3",
"@vue/test-utils": "^2.4.4",
"eslint": "^8.49.0",
"eslint-plugin-vue": "^9.17.0",
"jsdom": "^24.0.0",
"vite": "^5.0.11",
"vitest": "^1.2.2"
}
}

BIN
frontend/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

47
frontend/src/App.vue Normal file
View File

@ -0,0 +1,47 @@
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
<style scoped>
header {
line-height: 1.5;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
}
</style>

View File

@ -0,0 +1,86 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

View File

@ -0,0 +1,35 @@
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}

View File

@ -0,0 +1,44 @@
<script setup>
defineProps({
msg: {
type: String,
required: true
}
})
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
Youve successfully created a project with
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>.
</h3>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>

View File

@ -0,0 +1,88 @@
<script setup>
import WelcomeItem from './WelcomeItem.vue'
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
import CommunityIcon from './icons/IconCommunity.vue'
import SupportIcon from './icons/IconSupport.vue'
</script>
<template>
<WelcomeItem>
<template #icon>
<DocumentationIcon />
</template>
<template #heading>Documentation</template>
Vues
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
provides you with all information you need to get started.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<ToolingIcon />
</template>
<template #heading>Tooling</template>
This project is served and bundled with
<a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
recommended IDE setup is
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> +
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
you need to test your components and web pages, check out
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and
<a href="https://on.cypress.io/component" target="_blank" rel="noopener"
>Cypress Component Testing</a
>.
<br />
More instructions are available in <code>README.md</code>.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<EcosystemIcon />
</template>
<template #heading>Ecosystem</template>
Get official tools and libraries for your project:
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
you need more resources, we suggest paying
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
a visit.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<CommunityIcon />
</template>
<template #heading>Community</template>
Got stuck? Ask your question on
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
Discord server, or
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
>StackOverflow</a
>. You should also subscribe to
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow
the official
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
twitter account for latest news in the Vue world.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading>Support Vue</template>
As an independent project, Vue relies on community backing for its sustainability. You can help
us by
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
</WelcomeItem>
</template>

View File

@ -0,0 +1,87 @@
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped>
.item {
margin-top: 2rem;
display: flex;
position: relative;
}
.details {
flex: 1;
margin-left: 1rem;
}
i {
display: flex;
place-items: center;
place-content: center;
width: 32px;
height: 32px;
color: var(--color-text);
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 0.4rem;
color: var(--color-heading);
}
@media (min-width: 1024px) {
.item {
margin-top: 0;
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
}
i {
top: calc(50% - 25px);
left: -26px;
position: absolute;
border: 1px solid var(--color-border);
background: var(--color-background);
border-radius: 8px;
width: 50px;
height: 50px;
}
.item:before {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
bottom: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:after {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
top: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:first-of-type:before {
display: none;
}
.item:last-of-type:after {
display: none;
}
}
</style>

View File

@ -0,0 +1,11 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import HelloWorld from '../HelloWorld.vue'
describe('HelloWorld', () => {
it('renders properly', () => {
const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
expect(wrapper.text()).toContain('Hello Vitest')
})
})

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>

View File

@ -0,0 +1,19 @@
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>

6
frontend/src/main.js Normal file
View File

@ -0,0 +1,6 @@
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')

16
frontend/vite.config.js Normal file
View File

@ -0,0 +1,16 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})

14
frontend/vitest.config.js Normal file
View File

@ -0,0 +1,14 @@
import { fileURLToPath } from 'node:url'
import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
import viteConfig from './vite.config'
export default mergeConfig(
viteConfig,
defineConfig({
test: {
environment: 'jsdom',
exclude: [...configDefaults.exclude, 'e2e/*'],
root: fileURLToPath(new URL('./', import.meta.url))
}
})
)