Skip to content
Snippets Groups Projects
Commit cb7fe034 authored by Planoetscher Daniel (Student Com20)'s avatar Planoetscher Daniel (Student Com20)
Browse files

Introduction page for users without team

parent 43897140
No related branches found
No related tags found
No related merge requests found
Showing with 186 additions and 29 deletions
......@@ -8,6 +8,7 @@ import AppWrapper from 'pages/AppWrapper';
const Home = lazy(() => import('pages/Home'));
const Login = lazy(() => import('pages/Login'));
const Register = lazy(() => import('pages/Register'));
const Introduction = lazy(() => import('pages/Introduction'));
export default function App() {
return (
......@@ -16,6 +17,7 @@ export default function App() {
<Switch>
<LoginRoute path="/login" component={Login} />
<LoginRoute path="/register" component={Register} />
<Route path="/introduction" component={Introduction} />
<Route path={['/tasks', '/projects', '/stats', '/teams', '/settings']} component={AppWrapper} />
<Route path="/" component={Home} />
</Switch>
......
......@@ -21,13 +21,11 @@ export default function RegisterForm({ onSubmit }: Props) {
<TextInput
label="Username"
name="username"
color="dark"
onChange={setUsername}
/>
<TextInput
label="Password"
name="password"
color="dark"
type="password"
onChange={setPassword}
/>
......
......@@ -53,14 +53,12 @@ export default function RegisterForm({ onSubmit }: Props) {
<TextInput
label="Username"
name="username"
color="dark"
onChange={setUsername}
validation={validateUsername}
/>
<TextInput
label="Password"
name="password"
color="dark"
type="password"
onChange={setPassword}
validation={validatePassword}
......@@ -68,7 +66,6 @@ export default function RegisterForm({ onSubmit }: Props) {
<TextInput
label="Repeat password"
name="repeat-password"
color="dark"
type="password"
onChange={setRepeatedPassword}
compareValue={password}
......
import { FormEvent, MouseEventHandler, useCallback, useState } from 'react';
import TextInput from 'components/ui/TextInput';
import Button from 'components/ui/Button';
import './team-create.scss';
interface Props {
onSubmit?: (name: string) => void;
onBack?: MouseEventHandler
}
function validateName(name: string): string | null {
if (name && name.length > 0) {
return null;
}
return 'The name is required';
}
export default function TeamCreateForm({ onSubmit, onBack }: Props) {
const [name, setName] = useState('');
const handleSubmit = useCallback(async (e: FormEvent) => {
e.preventDefault();
if (validateName(name) === null) {
onSubmit?.(name);
}
}, [onSubmit, name]);
return (
<form className="team-create-form" onSubmit={handleSubmit}>
<div className="lead-text">
<p>
Create a new team with just one click by giving it a name!
</p>
</div>
<TextInput label="Team name" name="name" validation={validateName} onChange={setName} />
<div className="button-container">
{
onBack &&
<Button onClick={onBack} className="hollow">
Go Back
</Button>
}
<Button type="submit">
Create
</Button>
</div>
</form>
)
}
\ No newline at end of file
.team-create-form {
.lead-text {
font-size: 18px;
}
.button-container {
display: flex;
justify-content: space-around;
}
}
\ No newline at end of file
import { Route, RouteProps, useHistory } from 'react-router-dom';
import { isLoggedIn } from 'adapters/auth';
import { useEffect } from 'react';
import { getTeams } from 'adapters/team';
export default function ProtectedRoute(props: RouteProps) {
const history = useHistory();
useEffect(() => {
if (!isLoggedIn()) {
history.push('/login');
}
})
useEffect(() => {
getTeams().then((teams) => {
if(teams.length <= 0) {
history.push('/introduction');
}
}).catch(() => {
history.push('/introduction');
});
});
return (
<Route {...props} />
);
......
......@@ -35,4 +35,10 @@
background: darken(s.$primary, 40%);
box-shadow: 0px 5px 15px rgba(darken(s.$primary, 40%), 0.1);
}
&.hollow {
background: transparent;
color: s.$primary;
border: 3px solid s.$primary;
}
}
\ No newline at end of file
import { ReactNode } from "react";
import { MouseEventHandler, ReactNode } from "react";
import './button.scss';
......@@ -7,11 +7,12 @@ interface Props {
children: ReactNode;
type?: "button" | "submit" | "reset";
className?: string;
onClick?: MouseEventHandler
}
export default function Button({children, type, className}: Props) {
export default function Button({children, type, className, onClick}: Props) {
return (
<button className={"button " + (className || '')} type={type}>
<button className={"button " + (className || '')} type={type} onClick={onClick}>
{children}
</button>
);
......
......@@ -6,14 +6,13 @@ import './text-input.scss';
interface Props {
label: string,
name: string,
color?: 'dark'
type?: 'password' | 'textarea' | 'text',
compareValue?: string,
onChange: Dispatch<string>,
validation?: ((text: string) => Promise<string | null> | string | null) | ((value1: string, value2: string) => Promise<string | null> | string | null);
}
export default function TextInput({ label, name, type, color, onChange, validation, compareValue }: Props) {
export default function TextInput({ label, name, type, onChange, validation, compareValue }: Props) {
const [error, setError] = useState('');
const handleChange = useCallback((e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
......@@ -27,7 +26,7 @@ export default function TextInput({ label, name, type, color, onChange, validati
return (
<div className={'input-element' + (type === 'textarea' ? ' textarea' : '')}>
<div className={'input-field ' + (color ?? '')}>
<div className="input-field">
<label htmlFor={name}>{label}</label>
{
type === 'textarea' ?
......
@use 'styles/settings.scss'as s;
@use 'styles/mixins.scss'as mx;
......@@ -17,18 +16,6 @@
padding: 0 5px;
width: 100%;
&.dark {
textarea,
input {
color: s.$body-color;
background: rgba(0, 0, 0, 0.025);
}
&:before {
display: none;
}
}
label {
font-size: 16px;
position: absolute;
......@@ -54,11 +41,10 @@
position: relative;
display: block;
border-radius: 15px;
color: #fff;
color: s.$body-color;
font-weight: s.$weight-regular;
font-family: s.$body-font;
background: rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.025);
}
}
}
}
\ No newline at end of file
import './intro.scss';
import Page from 'components/layout/Page';
import Button from 'components/ui/Button';
import { getCurrentUser } from 'adapters/user';
import { useCallback, useEffect, useState } from 'react';
import TeamCreateForm from 'components/forms/TeamCreateForm';
import { createTeam } from 'adapters/team';
import { useHistory } from 'react-router';
export default function Introduction() {
const [username, setUsername] = useState('');
const [showForm, setShowForm] = useState(false);
const history = useHistory();
useEffect(() => {
getCurrentUser().then((user) => {
setUsername(user.username);
}).catch(() => {
history.push('/login');
});
});
const handleCreateTeam = useCallback(async (name: string) => {
try {
if (await createTeam(name)) {
history.push('/tasks');
}
} catch (e) { }
}, [history]);
return (
<div className="introduction-page-container">
<Page className="introduction-page">
<div className="content-container">
<h1 className="underlined">Welcome to ryoko</h1>
{!showForm && (
<div className="introduction-container">
<div className="lead-text">
You are one step away from getting started with ryoko.
</div>
<div className="possibility">
Ask one of your colleagues to add you to their team. <br />
Your username is: <strong>{username}</strong>
</div>
or
<div className="possibility">
<Button onClick={() => setShowForm(true)}>
Create a new Team
</Button>
</div>
</div>)
}
{
showForm &&
<TeamCreateForm onSubmit={handleCreateTeam} onBack={() => setShowForm(false)} />
}
</div>
</Page>
</div>
)
}
\ No newline at end of file
@use 'styles/settings.scss'as s;
@use 'styles/mixins.scss'as mx;
.introduction-page-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
@include mx.breakpoint(xlarge) {
min-height: calc(100vh - 5rem);
}
.introduction-page {
max-width: 960px;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
@include mx.breakpoint(medium) {
min-height: auto;
padding: 60px;
border-radius: 25px;
}
.lead-text {
font-size: 24px;
margin-bottom: 40px;
}
.possibility {
margin: 20px 0;
font-size: 18px;
}
}
}
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