mirror of
https://github.com/pikami/scrummie-poker.git
synced 2026-04-17 12:00:07 +01:00
Code cleanup
This commit is contained in:
@@ -4,11 +4,10 @@ import { getRouteApi } from '@tanstack/react-router';
|
||||
import TaskSidebar from './components/TaskSidebar';
|
||||
import VoteSelection from './components/VoteSelection';
|
||||
import VoteList from './components/VoteList';
|
||||
import { Drawer, Loader } from '../../components';
|
||||
import EditTicketForm from './components/EditTicketForm';
|
||||
import PlayerList from './components/PlayerList';
|
||||
import HtmlEmbed from '../../components/HtmlEmbed';
|
||||
import EstimationResult from './components/EstimationResult';
|
||||
import { Drawer, HtmlEmbed, Loader } from 'src/components';
|
||||
|
||||
const fibonacciSequence = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 100];
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import { Button, Input } from '../../../components';
|
||||
import RichEditor from '../../../components/RichEditor';
|
||||
import { Button, Input, RichEditor } from 'src/components';
|
||||
import { yupValidator } from '@tanstack/yup-form-adapter';
|
||||
import * as yup from 'yup';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import { Button, ButtonColor, Input } from '../../../components';
|
||||
import { PlayerVote } from '../../../lib/types/entityModels';
|
||||
import { Button, ButtonColor, Input } from 'src/components';
|
||||
import { PlayerVote } from 'src/lib/types/entityModels';
|
||||
import { yupValidator } from '@tanstack/yup-form-adapter';
|
||||
import * as yup from 'yup';
|
||||
|
||||
@@ -77,7 +77,6 @@ const EstimationResult: React.FC<VoteListProps> = ({
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
errors={field.state.meta.errors}
|
||||
/>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { EntityModels } from '../../../lib/types';
|
||||
import CopyInput from '../../../components/CopyInput';
|
||||
import { EntityModels } from 'src/lib/types';
|
||||
import { CopyInput } from 'src/components';
|
||||
|
||||
interface PlayerListProps {
|
||||
sessionId: string;
|
||||
@@ -14,7 +14,7 @@ const PlayerList: React.FC<PlayerListProps> = ({
|
||||
title = 'Players',
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex w-full max-w-sm flex-col justify-between rounded-lg bg-white p-6 shadow-lg dark:bg-nero-800">
|
||||
<div className="flex w-full max-w-sm flex-col justify-between bg-white p-6 shadow-lg dark:bg-nero-800">
|
||||
<h2 className="mb-4 text-xl font-semibold text-gray-900 dark:text-gray-100">
|
||||
{title}
|
||||
</h2>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import classNames from 'classnames';
|
||||
import { Button, Card, Drawer, GridList } from '../../../components';
|
||||
import { EstimationSessionTicket } from '../../../lib/types/entityModels';
|
||||
import { Button, Card, Drawer, GridList } from 'src/components';
|
||||
import { EstimationSessionTicket } from 'src/lib/types/entityModels';
|
||||
import TicketImportForm from './TicketImportForm';
|
||||
import { useState } from 'react';
|
||||
|
||||
|
||||
@@ -2,21 +2,36 @@ import { useState } from 'react';
|
||||
import {
|
||||
handleTicketFileUpload,
|
||||
TicketFileUploadResponse,
|
||||
} from '../../../lib/parsers/ticketUpload';
|
||||
import { EstimationSessionTicket } from '../../../lib/types/entityModels';
|
||||
import { Button, Card, GridList, Loader } from '../../../components';
|
||||
import HtmlEmbed from '../../../components/HtmlEmbed';
|
||||
import { useEstimationContext } from '../../../lib/context/estimation';
|
||||
} from 'src/lib/parsers/ticketUpload';
|
||||
import { EntityModels } from 'src/lib/types';
|
||||
import { Button, Card, GridList, HtmlEmbed, Loader } from 'src/components';
|
||||
import { useEstimationContext } from 'src/lib/context/estimation';
|
||||
|
||||
interface TicketImportFormProps {
|
||||
onTicketsImported: () => void;
|
||||
}
|
||||
|
||||
const TicketItem = ({
|
||||
item,
|
||||
}: {
|
||||
item: EntityModels.EstimationSessionTicket;
|
||||
}) => (
|
||||
<Card
|
||||
key={item.id}
|
||||
title={item.name}
|
||||
description={`Estimate: ${item.estimate ? item.estimate : 'N/A'}`}
|
||||
>
|
||||
<HtmlEmbed className="h-16 w-full" body={item.content} />
|
||||
</Card>
|
||||
);
|
||||
|
||||
const TicketImportForm: React.FC<TicketImportFormProps> = ({
|
||||
onTicketsImported,
|
||||
}) => {
|
||||
const [error, setError] = useState<string>('');
|
||||
const [tickets, setTickets] = useState<EstimationSessionTicket[]>([]);
|
||||
const [tickets, setTickets] = useState<
|
||||
EntityModels.EstimationSessionTicket[]
|
||||
>([]);
|
||||
const estimationContext = useEstimationContext();
|
||||
|
||||
if (!estimationContext) {
|
||||
@@ -65,15 +80,7 @@ const TicketImportForm: React.FC<TicketImportFormProps> = ({
|
||||
className="no-scrollbar overflow-y-scroll"
|
||||
items={tickets}
|
||||
colNum={1}
|
||||
itemComponent={({ item }) => (
|
||||
<Card
|
||||
key={item.id}
|
||||
title={item.name}
|
||||
description={`Estimate: ${item.estimate || 'N/A'}`}
|
||||
>
|
||||
<HtmlEmbed className="h-16 w-full" body={item.content} />
|
||||
</Card>
|
||||
)}
|
||||
itemComponent={TicketItem}
|
||||
/>
|
||||
<Button className="mt-4" fullWidth onClick={onCreateTickets}>
|
||||
Import
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Card, GridList } from '../../../components';
|
||||
import { PlayerVote } from '../../../lib/types/entityModels';
|
||||
import { Card, GridList } from 'src/components';
|
||||
import { PlayerVote } from 'src/lib/types/entityModels';
|
||||
|
||||
interface VoteListProps {
|
||||
className?: string;
|
||||
@@ -8,22 +8,20 @@ interface VoteListProps {
|
||||
}
|
||||
|
||||
const VoteList: React.FC<VoteListProps> = ({ className, votes, revealed }) => {
|
||||
const voteListItem = ({ item }: { item: PlayerVote }, idx: string) => (
|
||||
<Card
|
||||
key={idx}
|
||||
title={item.username}
|
||||
description={revealed ? item.estimate : 'Hidden'}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{votes.length > 0 && (
|
||||
<h2 className="mb-4 text-xl font-bold">Player Votes</h2>
|
||||
)}
|
||||
<GridList
|
||||
colNum={5}
|
||||
itemComponent={({ item }, idx) => (
|
||||
<Card
|
||||
key={idx}
|
||||
title={item.username}
|
||||
description={revealed ? item.estimate : 'Hidden'}
|
||||
/>
|
||||
)}
|
||||
items={votes}
|
||||
/>
|
||||
<GridList colNum={5} itemComponent={voteListItem} items={votes} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import classNames from 'classnames';
|
||||
import { Button } from 'src/components';
|
||||
|
||||
interface VoteSelectionProps {
|
||||
className: string;
|
||||
@@ -15,20 +16,20 @@ const VoteSelection: React.FC<VoteSelectionProps> = ({
|
||||
}) => {
|
||||
const getItemClassName = (option: string) =>
|
||||
classNames('rounded-md px-4 py-2 text-white transition-colors', {
|
||||
'bg-indigo-800': value !== option,
|
||||
'bg-indigo-600 hover:bg-indigo-500': value === option,
|
||||
'bg-indigo-800': value === option,
|
||||
'bg-indigo-600 hover:bg-indigo-500': value !== option,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{options.map((option) => (
|
||||
<button
|
||||
<Button
|
||||
key={option}
|
||||
className={getItemClassName(option)}
|
||||
onClick={() => onSelect(option)}
|
||||
className={getItemClassName(option)}
|
||||
>
|
||||
{option}
|
||||
</button>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import { getRouteApi } from '@tanstack/react-router';
|
||||
import { useEstimationsList } from '../lib/context/estimationsList';
|
||||
import {
|
||||
Card,
|
||||
CreateEstimationSessionForm,
|
||||
Drawer,
|
||||
GridList,
|
||||
} from '../components';
|
||||
import { useState } from 'react';
|
||||
|
||||
const route = getRouteApi('/_authenticated/');
|
||||
|
||||
function Home() {
|
||||
const navigate = route.useNavigate();
|
||||
const estimationsList = useEstimationsList();
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-3xl font-bold">Estimation sessions</h1>
|
||||
|
||||
<GridList
|
||||
colNum={2}
|
||||
className="my-3"
|
||||
items={estimationsList?.current ?? []}
|
||||
itemComponent={({ item }) => (
|
||||
<Card
|
||||
key={item.id}
|
||||
title={item.name}
|
||||
onClick={() => {
|
||||
navigate({
|
||||
to: '/estimate/session/$sessionId',
|
||||
params: { sessionId: item.id },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
addItemLabel="+ Create Estimation Session"
|
||||
onAddItem={() => setIsDrawerOpen(true)}
|
||||
/>
|
||||
|
||||
<Drawer isOpen={isDrawerOpen} onClose={() => setIsDrawerOpen(false)}>
|
||||
<CreateEstimationSessionForm onCreated={() => setIsDrawerOpen(false)} />
|
||||
</Drawer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
35
src/pages/Home/Home.tsx
Normal file
35
src/pages/Home/Home.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { useEstimationsList } from 'src/lib/context/estimationsList';
|
||||
import { Drawer, DrawerSize, GridList } from 'src/components';
|
||||
import { useState } from 'react';
|
||||
import CreateEstimationSessionForm from './components/CreateEstimationSessionForm';
|
||||
import EstimationSessionCard from './components/EstimationSessionCard';
|
||||
|
||||
function Home() {
|
||||
const estimationsList = useEstimationsList();
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-3xl font-bold">Estimation sessions</h1>
|
||||
|
||||
<GridList
|
||||
colNum={2}
|
||||
className="my-3"
|
||||
items={estimationsList?.current ?? []}
|
||||
itemComponent={EstimationSessionCard}
|
||||
addItemLabel="+ Create Estimation Session"
|
||||
onAddItem={() => setIsDrawerOpen(true)}
|
||||
/>
|
||||
|
||||
<Drawer
|
||||
size={DrawerSize.Small}
|
||||
isOpen={isDrawerOpen}
|
||||
onClose={() => setIsDrawerOpen(false)}
|
||||
>
|
||||
<CreateEstimationSessionForm onCreated={() => setIsDrawerOpen(false)} />
|
||||
</Drawer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
77
src/pages/Home/components/CreateEstimationSessionForm.tsx
Normal file
77
src/pages/Home/components/CreateEstimationSessionForm.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import { useEstimationsList } from 'src/lib/context/estimationsList';
|
||||
import { yupValidator } from '@tanstack/yup-form-adapter';
|
||||
import * as yup from 'yup';
|
||||
import { Input, Button } from 'src/components';
|
||||
|
||||
interface CreateEstimationSessionFormProps {
|
||||
onCreated: () => void;
|
||||
}
|
||||
|
||||
const CreateEstimationSessionForm: React.FC<
|
||||
CreateEstimationSessionFormProps
|
||||
> = ({ onCreated }) => {
|
||||
const estimationsList = useEstimationsList();
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
await estimationsList?.add({
|
||||
name: value.name,
|
||||
});
|
||||
onCreated();
|
||||
},
|
||||
validators: {
|
||||
onChange: yup.object({
|
||||
name: yup.string().label('Name').max(200).required(),
|
||||
}),
|
||||
},
|
||||
validatorAdapter: yupValidator(),
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="mb-4 text-xl font-bold">
|
||||
Create a New Estimation Session
|
||||
</h2>
|
||||
<form
|
||||
className="space-y-6"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
>
|
||||
<form.Field name="name">
|
||||
{(field) => (
|
||||
<Input
|
||||
label="Name"
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
errors={field.state.meta.errors}
|
||||
/>
|
||||
)}
|
||||
</form.Field>
|
||||
<form.Subscribe
|
||||
selector={(state) => [state.canSubmit, state.isSubmitting]}
|
||||
>
|
||||
{([canSubmit, isSubmitting]) => (
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!canSubmit}
|
||||
isLoading={isSubmitting}
|
||||
fullWidth
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
)}
|
||||
</form.Subscribe>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateEstimationSessionForm;
|
||||
30
src/pages/Home/components/EstimationSessionCard.tsx
Normal file
30
src/pages/Home/components/EstimationSessionCard.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { getRouteApi } from '@tanstack/react-router';
|
||||
import { Card } from 'src/components';
|
||||
import { EntityModels } from 'src/lib/types';
|
||||
|
||||
interface EstimationSessionCardProps {
|
||||
item: EntityModels.EstimationSession;
|
||||
}
|
||||
|
||||
const route = getRouteApi('/_authenticated/');
|
||||
|
||||
const EstimationSessionCard: React.FC<EstimationSessionCardProps> = ({
|
||||
item,
|
||||
}) => {
|
||||
const navigate = route.useNavigate();
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={item.id}
|
||||
title={item.name}
|
||||
onClick={() => {
|
||||
navigate({
|
||||
to: '/estimate/session/$sessionId',
|
||||
params: { sessionId: item.id },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default EstimationSessionCard;
|
||||
@@ -3,9 +3,9 @@ import {
|
||||
getInviteInfo,
|
||||
joinSession,
|
||||
SessionInviteInfo,
|
||||
} from '../lib/functions/estimationSessionInvite';
|
||||
} from 'src/lib/functions/estimationSessionInvite';
|
||||
import { getRouteApi } from '@tanstack/react-router';
|
||||
import { Loader } from '../components';
|
||||
import { Button, ButtonColor, Card, Loader } from 'src/components';
|
||||
|
||||
const route = getRouteApi('/_authenticated/join/$sessionId');
|
||||
|
||||
@@ -50,28 +50,21 @@ const Join = () => {
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center bg-gray-50 transition-colors dark:bg-nero-900">
|
||||
<div className="max-w-lg rounded-lg bg-white p-8 text-center shadow-lg dark:bg-nero-800">
|
||||
<h1 className="mb-4 text-2xl font-semibold text-gray-900 dark:text-gray-100">
|
||||
You have been invited to join a new estimation session!
|
||||
</h1>
|
||||
<Card
|
||||
title="You have been invited to join a new estimation session!"
|
||||
className="bg-white shadow-lg dark:bg-nero-800"
|
||||
transparent
|
||||
>
|
||||
<p className="mb-6 text-lg text-gray-700 dark:text-gray-300">
|
||||
Session Name: <strong>{sessionInfo.name}</strong>
|
||||
</p>
|
||||
<div className="flex justify-center gap-4">
|
||||
<button
|
||||
onClick={handleAccept}
|
||||
className="rounded-md bg-indigo-600 px-6 py-2 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-indigo-500"
|
||||
>
|
||||
Accept
|
||||
</button>
|
||||
<button
|
||||
onClick={handleReturnHome}
|
||||
className="rounded-md bg-gray-300 px-6 py-2 text-sm font-semibold text-gray-900 shadow-sm transition-colors hover:bg-gray-200 dark:bg-nero-700 dark:text-gray-100 dark:hover:bg-gray-600"
|
||||
>
|
||||
Return to Home
|
||||
</button>
|
||||
<Button onClick={handleAccept}>Accept</Button>
|
||||
<Button onClick={handleReturnHome} color={ButtonColor.Secondary}>
|
||||
Return Home
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import { useUser } from '../lib/context/user';
|
||||
import { Button, ButtonColor, Input } from '../components';
|
||||
import { useUser } from 'src/lib/context/user';
|
||||
import { Button, ButtonColor, Card, Input } from 'src/components';
|
||||
import { yupValidator } from '@tanstack/yup-form-adapter';
|
||||
import * as yup from 'yup';
|
||||
import { Link } from '@tanstack/react-router';
|
||||
|
||||
const Login = () => {
|
||||
const user = useUser();
|
||||
@@ -24,14 +25,11 @@ const Login = () => {
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-sm">
|
||||
<h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight">
|
||||
Sign in to your account
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
|
||||
<div className="flex h-screen flex-1 flex-col justify-center px-6 py-12 lg:px-8">
|
||||
<Card
|
||||
title="Sign in to your account"
|
||||
className="sm:mx-auto sm:w-full sm:max-w-sm"
|
||||
>
|
||||
<form className="space-y-6">
|
||||
<form.Field name="email">
|
||||
{(field) => (
|
||||
@@ -107,15 +105,14 @@ const Login = () => {
|
||||
|
||||
<p className="mt-10 text-center text-sm text-gray-500">
|
||||
Don't want to create an account?{' '}
|
||||
<a
|
||||
href="#"
|
||||
<Link
|
||||
className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"
|
||||
onClick={() => user.loginAsGuest()}
|
||||
>
|
||||
Sign in as a guest
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import { Button, Input } from '../components';
|
||||
import { useUser } from '../lib/context/user';
|
||||
import { Button, Card, Input } from 'src/components';
|
||||
import { useUser } from 'src/lib/context/user';
|
||||
import { yupValidator } from '@tanstack/yup-form-adapter';
|
||||
import * as yup from 'yup';
|
||||
|
||||
@@ -15,7 +15,7 @@ const Profile = () => {
|
||||
updateUsernameForm.reset();
|
||||
},
|
||||
validators: {
|
||||
onChange: yup.object({
|
||||
onSubmit: yup.object({
|
||||
name: yup.string().label('Name').max(128).required(),
|
||||
}),
|
||||
},
|
||||
@@ -24,10 +24,11 @@ const Profile = () => {
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gray-50 transition-colors dark:bg-nero-900">
|
||||
<div className="w-full max-w-md rounded-lg bg-white p-8 shadow-lg dark:bg-nero-800">
|
||||
<h1 className="mb-6 text-2xl font-semibold text-gray-900 dark:text-gray-100">
|
||||
Update Name
|
||||
</h1>
|
||||
<Card
|
||||
title="Update Name"
|
||||
className="w-full max-w-md bg-white shadow-lg dark:bg-nero-800"
|
||||
transparent
|
||||
>
|
||||
<form
|
||||
className="space-y-6"
|
||||
onSubmit={(e) => {
|
||||
@@ -65,7 +66,7 @@ const Profile = () => {
|
||||
)}
|
||||
</updateUsernameForm.Subscribe>
|
||||
</form>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
7
src/pages/index.ts
Normal file
7
src/pages/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import Estimation from './Estimation/Estimation';
|
||||
import Home from './Home/Home';
|
||||
import Join from './Join';
|
||||
import Login from './Login';
|
||||
import Profile from './Profile';
|
||||
|
||||
export { Estimation, Home, Join, Login, Profile };
|
||||
Reference in New Issue
Block a user