Skip to content
Snippets Groups Projects
Commit c871dd3e authored by Bernard Roland (Student Com20)'s avatar Bernard Roland (Student Com20)
Browse files

Merge branch 'frontend-devel'

parents f628d022 704ae220
No related branches found
No related tags found
No related merge requests found
Showing
with 123 additions and 54 deletions
client/public/favicon.ico

66.1 KiB | W: | H:

client/public/favicon.ico

7.5 KiB | W: | H:

client/public/favicon.ico
client/public/favicon.ico
client/public/favicon.ico
client/public/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
client/public/logo192.png

6.49 KiB | W: | H:

client/public/logo192.png

3.72 KiB | W: | H:

client/public/logo192.png
client/public/logo192.png
client/public/logo192.png
client/public/logo192.png
  • 2-up
  • Swipe
  • Onion skin
client/public/logo192_maskable.png

4.6 KiB | W: | H:

client/public/logo192_maskable.png

2.74 KiB | W: | H:

client/public/logo192_maskable.png
client/public/logo192_maskable.png
client/public/logo192_maskable.png
client/public/logo192_maskable.png
  • 2-up
  • Swipe
  • Onion skin
client/public/logo512.png

22.6 KiB | W: | H:

client/public/logo512.png

10.4 KiB | W: | H:

client/public/logo512.png
client/public/logo512.png
client/public/logo512.png
client/public/logo512.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -6,7 +6,7 @@ import AppWrapper from 'pages/AppWrapper';
import LoginRoute from 'components/helpers/LoginRoute';
import ProtectedRoute from 'components/helpers/ProtectedRoute';
const Home = lazy(() => import('pages/Home'));
const Landing = lazy(() => import('pages/Landing'));
const Login = lazy(() => import('pages/Login'));
const Register = lazy(() => import('pages/Register'));
const Introduction = lazy(() => import('pages/Introduction'));
......@@ -20,7 +20,7 @@ export default function App() {
<LoginRoute path="/register" component={Register} />
<Route path="/introduction" component={Introduction} />
<ProtectedRoute path={['/tasks', '/projects', '/stats', '/teams', '/settings']} component={AppWrapper} />
<Route path="/" component={Home} />
<Route path="/" component={Landing} />
</Switch>
</Suspense>
</Router>
......
import { useCallback, useState } from "react";
import Popup from 'components/ui/Popup';
import Button from 'components/ui/Button';
import TimeInput from "components/ui/TimeInput";
import '../form.scss';
interface Props {
onAssign: (duration: number) => any;
initialTime?: number;
}
export default function AssignForm({ onAssign, initialTime }: Props) {
const [popup, setPopup] = useState(false);
const [selectedTime, setSelectedTime] = useState<number | undefined>(initialTime);
const addAssignee = useCallback((e) => {
e.preventDefault();
setPopup(false);
if (selectedTime && !Number.isNaN(selectedTime)) {
onAssign(selectedTime * 60);
} else {
onAssign(0);
}
}, [onAssign, selectedTime])
return <>
<Button className="expanded dark" onClick={() => setPopup(true)}>
{initialTime ? 'Change assignment' : 'Assign yourself'}
</Button>
{
popup && (
<Popup onClose={() => setPopup(false)}>
<form onSubmit={addAssignee}>
<TimeInput initialTime={initialTime && (initialTime / 60)} onChange={value => setSelectedTime(value)} />
<div>
<Button type="submit" className="expanded dark">
{initialTime ? 'Change assignment' : 'Assign yourself'}
</Button>
</div>
</form>
</Popup>
)
}
</>;
}
......@@ -37,6 +37,8 @@
align-items: center;
font-size: 24px;
font-weight: s.$weight-bold;
height: calc(2em + 40px);
user-select: none;
}
}
}
......
......@@ -2,13 +2,15 @@
import { useCallback, useEffect, useState } from "react";
import { TaskAssignment } from "adapters/task";
import { durationFor, formatDuration } from "timely";
import Popup from 'components/ui/Popup';
import Button from 'components/ui/Button';
import { PossibleMember } from "components/forms/TaskForm";
import TimeInput from "components/ui/TimeInput";
import { PossibleMember } from "components/forms/TaskForm";
import './assignees-form.scss';
import '../form.scss';
interface Props {
assignees: TaskAssignment[];
......@@ -56,7 +58,9 @@ export default function AssigneesForm({ assignees, members, onNew, onDelete }: P
<div className="person">
{members.find(member => member.id === assignee.user)?.label}
</div>
<div className="time">{assignee.time} min</div>
<div className="time">{
formatDuration(durationFor(assignee.time, 'minute'), 'second', 2, true)
}</div>
<div className="delete" onClick={() => removeAssignee(assignee.user)}>
<span className="material-icons">
clear
......@@ -76,18 +80,20 @@ export default function AssigneesForm({ assignees, members, onNew, onDelete }: P
{
addNew && (
<Popup onClose={() => setAddNew(false)}>
<select onChange={(e) => setSelectedMember(e.target.value)}>
<option value="">Please select a user</option>
{
possibleMembers.map((member) => (
<option value={member.id} key={member.id}>{member.label}</option>
))
}
</select>
<TimeInput onChange={value => setSelectedTime(value)} />
<Button type="submit" onClick={addAssignee} className="expanded">
Add the assignee
</Button>
<form>
<select onChange={(e) => setSelectedMember(e.target.value)}>
<option value="" selected disabled hidden>Please select a user</option>
{
possibleMembers.map((member) => (
<option value={member.id} key={member.id}>{member.label}</option>
))
}
</select>
<TimeInput onChange={value => setSelectedTime(value)} />
<Button type="submit" onClick={addAssignee} className="expanded">
Add the assignee
</Button>
</form>
</Popup>
)
}
......
@use 'styles/settings' as s;
@use 'styles/mixins' as mx;
@use 'styles/settings'as s;
@use 'styles/mixins'as mx;
.contact-form {
.fields {
......@@ -11,19 +10,12 @@
&.textarea {
width: 100%;
}
.input-field {
input, textarea {
background-color: #ffffff20;
color: white;
}
}
}
.submit-button {
appearance: none;
border: none;
margin: 0.5rem;
margin: 0;
}
.button-container {
......@@ -31,5 +23,4 @@
width: 100%;
justify-content: flex-end;
}
}
}
\ No newline at end of file
......@@ -6,6 +6,7 @@ import TextInput from 'components/ui/TextInput';
import LoadingScreen from 'components/ui/LoadingScreen';
import './login-form.scss';
import '../form.scss';
interface Props {
onSubmit?: (username: string, password: string) => Promise<void>
......
......@@ -31,7 +31,7 @@ export default function UsernameForm({ setResult }: Props) {
name="name"
onChange={setUsername}
/>
<Button type="submit">
<Button type="submit" className="expanded">
Add user
</Button>
</form >
......
......@@ -9,6 +9,7 @@ import RoleForm from 'components/forms/RoleForm';
import UsernameForm from 'components/forms/MemberForm/UsernameForm';
import './member-form.scss';
import '../form.scss';
interface Props {
roles: TeamRole[];
......
......@@ -13,6 +13,7 @@ import CheckboxGroup from 'components/ui/CheckboxGroup';
import '../form.scss';
import './project-form.scss';
import { formatDateShort } from 'timely';
interface Props {
project?: Project
......@@ -63,7 +64,7 @@ export default function ProjectForm({ project, onSubmit }: Props) {
const [text, setText] = useState(project?.text);
const [status, setStatus] = useState(project?.status);
const [color, setColor] = useState(project?.color);
const [deadline, setDeadline] = useState(project?.deadline?.toISOString());
const [deadline, setDeadline] = useState(project?.deadline ? formatDateShort(new Date(project?.deadline)) : '');
const [error, setError] = useState('');
const [loadError, setLoadError] = useState(false);
const [teams, setTeams] = useState(project?.teams ?? []);
......@@ -85,7 +86,7 @@ export default function ProjectForm({ project, onSubmit }: Props) {
}
setAllTeams(teams);
})
.catch(() => setLoadError(true))
.catch(() => setLoadError(true))
}, [project?.teams, loadError])
const colors = Object.values(ProjectColors);
......@@ -105,6 +106,7 @@ export default function ProjectForm({ project, onSubmit }: Props) {
}
}, [onSubmit, setError, name, text, color, deadline, teams, status]);
return (
<form onSubmit={handleSubmit} className={'project-form theme-' + color}>
{error && <Callout message={error} />}
......@@ -141,12 +143,12 @@ export default function ProjectForm({ project, onSubmit }: Props) {
status &&
<div className="field">
<label className="field-label" htmlFor="status">Status</label>
<select id="status" defaultValue={project?.status} onChange={(e) => {
<select id="status" defaultValue={project?.status ?? ''} onChange={(e) => {
let currentStatus = Object.values(Status).find(s => s === e.target.value) ?? undefined;
setStatus(currentStatus);
}
}>
<option value="">Please choose a status</option>
<option value="" disabled hidden>Please choose a status</option>
{
allStatus.map((s) => (
<option value={s} key={s}>{s}</option>
......@@ -174,7 +176,7 @@ export default function ProjectForm({ project, onSubmit }: Props) {
<div className="teams">
<h2>Teams</h2>
<p>Which ones of your teams are working on this project</p>
{ loadError
{loadError
? <ErrorScreen />
: <CheckboxGroup choices={allTeams} chosen={teams} setChosen={setTeams} />
}
......
......@@ -22,6 +22,7 @@
position: relative;
filter: saturate(50%);
transform: scale(90%);
will-change: transform;
@include mx.breakpoint(small) {
width: calc(100% / 5 - 10px);
......
......@@ -8,6 +8,7 @@ import TextInput from 'components/ui/TextInput';
import LoadingScreen from 'components/ui/LoadingScreen';
import './register-form.scss';
import '../form.scss';
async function validateUsername(username: string) {
if (username?.length < 3) {
......
......@@ -2,6 +2,7 @@
import { useCallback, useEffect, useState } from 'react';
import { TaskRequirement } from 'adapters/task';
import { durationFor, formatDuration } from 'timely';
import { PossibleRole } from 'components/forms/TaskForm';
import Popup from 'components/ui/Popup';
......@@ -9,6 +10,7 @@ import Button from 'components/ui/Button';
import TimeInput from 'components/ui/TimeInput'
import './requirements-form.scss';
import '../form.scss';
interface Props {
roles: PossibleRole[],
......@@ -52,7 +54,9 @@ export default function RequirementsForm({ roles, requirements, onNew, onDelete
requirements.map((requirement) => (
<div className="requirement" key={requirement.role}>
<div>{roles.find(role => role.id === requirement.role)?.label}</div>
<div>{requirement.time} min</div>
<div>{
formatDuration(durationFor(requirement.time, 'minute'), 'second', 2, true)
}</div>
<div className="delete" onClick={() => removeRequirement(requirement.role)}>
<span className="material-icons">
clear
......@@ -72,18 +76,20 @@ export default function RequirementsForm({ roles, requirements, onNew, onDelete
{
addNew && (
<Popup onClose={() => setAddNew(false)}>
<select onChange={(e) => setSelectedRole(e.target.value)}>
<option value="">Please select a role</option>
{
possibleRoles.map((role) => (
<option value={role.id} key={role.id}>{role.label}</option>
))
}
</select>
<TimeInput onChange={value => setSelectedTime(value)} />
<Button type="submit" onClick={addRequirement} className="expanded">
Create new requirement
</Button>
<form>
<select onChange={(e) => setSelectedRole(e.target.value)}>
<option value="" selected disabled hidden>Please select a role</option>
{
possibleRoles.map((role) => (
<option value={role.id} key={role.id}>{role.label}</option>
))
}
</select>
<TimeInput onChange={value => setSelectedTime(value)} />
<Button type="submit" onClick={addRequirement} className="expanded">
Create new requirement
</Button>
</form>
</Popup>
)
}
......
......@@ -37,6 +37,8 @@
align-items: center;
font-size: 24px;
font-weight: s.$weight-bold;
height: calc(2em + 40px);
user-select: none;
}
}
}
......
......@@ -7,6 +7,7 @@ import RoleChangeForm from 'components/forms/RoleForm/RoleChangeForm';
import RoleEditForm from 'components/forms/RoleForm/RoleEditForm';
import './role-form.scss';
import '../form.scss'
interface Props {
roles: TeamRole[];
......
......@@ -5,8 +5,11 @@
display: flex;
flex-direction: column;
h2 {
margin-top: 0;
}
.role-item {
background: s.$background-light;
background: s.$background-input;
border-radius: 25px;
font-size: 18px;
width: 100%;
......@@ -99,6 +102,9 @@
justify-content: center;
align-items: center;
font-size: 28px;
margin-bottom: 24px;
font-size: 36px;
font-weight: s.$weight-semi-bold;
}
button {
......@@ -109,6 +115,7 @@
.role-edit-form {
h2 {
margin-bottom: 40px;
margin-top: 0;
}
.button {
......
......@@ -185,11 +185,11 @@ export default function TaskForm({ task, onSubmit, project }: Props) {
<label className="field-label" htmlFor="status">
Priority
</label>
<select defaultValue={priority} onChange={(e) => {
<select defaultValue={priority ?? ''} onChange={(e) => {
let currentPriority = Object.values(Priority).find(s => s === e.target.value) ?? undefined;
setPriority(currentPriority);
}}>
<option value={''}>Please choose a priority</option>
<option value="" disabled hidden>Please choose a priority</option>
{
allPriorities.map((priority) => (
<option value={priority} key={priority}>{priority}</option>
......@@ -206,16 +206,16 @@ export default function TaskForm({ task, onSubmit, project }: Props) {
</div>
<div className="col">
{
status && (
task && (
<div className="field">
<label className="field-label" htmlFor="status">
Status
</label>
<select defaultValue={status} id="status" onChange={(e) => {
<select defaultValue={status ?? ''} id="status" onChange={(e) => {
let currentStatus = Object.values(Status).find(s => s === e.target.value) ?? undefined;
setStatus(currentStatus);
}}>
<option value={''}>Please choose a status</option>
<option value="" disabled hidden>Please choose a status</option>
{
allStatus.map((status) => (
<option value={status} key={status}>{status}</option>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment