mirror of
https://github.com/pikami/scrummie-poker.git
synced 2026-04-17 03:49:23 +01:00
Write a POC of estimation session
This commit is contained in:
48
src/pages/CreateEstimationSession.tsx
Normal file
48
src/pages/CreateEstimationSession.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import { useEstimationSessions } from '../lib/context/estimationSession';
|
||||
import { useUser } from '../lib/context/user';
|
||||
|
||||
const CreateEstimationSession = () => {
|
||||
const user = useUser();
|
||||
const estimationSessions = useEstimationSessions();
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
await estimationSessions?.add({
|
||||
Name: value.name,
|
||||
UserId: user.current?.$id,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Create Estimation Session</h1>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
>
|
||||
<form.Field
|
||||
name="name"
|
||||
children={(field) => (
|
||||
<input
|
||||
placeholder="Name"
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateEstimationSession;
|
||||
110
src/pages/EstimationSession.tsx
Normal file
110
src/pages/EstimationSession.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import { getRouteApi } from '@tanstack/react-router';
|
||||
import { useEstimationSessions } from '../lib/context/estimationSession';
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import { useUser } from '../lib/context/user';
|
||||
|
||||
const route = getRouteApi('/estimate/session/$sessionId');
|
||||
|
||||
const EstimationSession = () => {
|
||||
const { sessionId } = route.useParams();
|
||||
const user = useUser();
|
||||
const estimationSessions = useEstimationSessions();
|
||||
const estimationSession = estimationSessions?.current.find(
|
||||
(x) => x.$id == sessionId,
|
||||
);
|
||||
const tickets = estimationSessions?.getTickets(sessionId);
|
||||
const currentState = estimationSessions?.getState(sessionId);
|
||||
|
||||
const createTicketForm = useForm({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
await estimationSessions?.addTicket(sessionId, {
|
||||
Name: value.name,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Estimation Session - {estimationSession?.Name}</h1>
|
||||
<div>
|
||||
<h2>Tasks</h2>
|
||||
{tickets?.map((x) => (
|
||||
<div key={x.Id}>
|
||||
{x.Id} - {x.Name}
|
||||
<button
|
||||
onClick={() => estimationSessions?.selectTicket(sessionId, x.Id)}
|
||||
>
|
||||
Select
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
createTicketForm.handleSubmit();
|
||||
}}
|
||||
>
|
||||
<createTicketForm.Field
|
||||
name="name"
|
||||
children={(field) => (
|
||||
<input
|
||||
placeholder="Name"
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
{currentState?.CurrentTicketId && (
|
||||
<div>
|
||||
<h2>
|
||||
{currentState.CurrentTicketId} -{' '}
|
||||
{tickets?.find((x) => x.Id === currentState.CurrentTicketId)?.Name}
|
||||
</h2>
|
||||
{[0.5, 1, 2, 3, 5, 8, 13, 21].map((estimate) => (
|
||||
<button
|
||||
key={estimate}
|
||||
onClick={() =>
|
||||
estimationSessions?.voteEstimate(
|
||||
sessionId,
|
||||
currentState.CurrentTicketId,
|
||||
estimate,
|
||||
user.current?.$id ?? '',
|
||||
)
|
||||
}
|
||||
>
|
||||
{estimate}
|
||||
</button>
|
||||
))}
|
||||
{currentState.VotesRevealed ? (
|
||||
<>
|
||||
<h3>Votes</h3>
|
||||
<ul>
|
||||
{currentState.Votes.map((vote) => (
|
||||
<li key={vote.UserId}>
|
||||
{vote.UserId} - {vote.Estimate}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
) : (
|
||||
<button onClick={() => estimationSessions?.revealVotes(sessionId)}>
|
||||
Reveal Votes
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<pre>Session Id: {sessionId}</pre>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EstimationSession;
|
||||
34
src/pages/Home.css
Normal file
34
src/pages/Home.css
Normal file
@@ -0,0 +1,34 @@
|
||||
#root {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
a:nth-of-type(2) .logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
46
src/pages/Home.tsx
Normal file
46
src/pages/Home.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import reactLogo from '../assets/react.svg';
|
||||
import viteLogo from '/vite.svg';
|
||||
import './Home.css';
|
||||
import { Link } from '@tanstack/react-router';
|
||||
import { useUser } from '../lib/context/user';
|
||||
import { useEstimationSessions } from '../lib/context/estimationSession';
|
||||
|
||||
function Home() {
|
||||
const user = useUser();
|
||||
const estimationSessions = useEstimationSessions();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<a href="https://vitejs.dev" target="_blank">
|
||||
<img src={viteLogo} className="logo" alt="Vite logo" />
|
||||
</a>
|
||||
<a href="https://react.dev" target="_blank">
|
||||
<img src={reactLogo} className="logo react" alt="React logo" />
|
||||
</a>
|
||||
</div>
|
||||
<h1>Scrummie-Poker</h1>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<Link to="/login">Login</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/estimate/new">Create Estimation Session</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<pre>User Id: {user.current?.$id}</pre>
|
||||
|
||||
<div>
|
||||
<p>Estimation sessions</p>
|
||||
{estimationSessions?.current.map((session) => (
|
||||
<Link key={session.$id} to={`/estimate/session/${session.$id}`}>
|
||||
{session.Name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
72
src/pages/Login.tsx
Normal file
72
src/pages/Login.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import { useUser } from '../lib/context/user';
|
||||
|
||||
const Login = () => {
|
||||
const user = useUser();
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
email: '',
|
||||
password: '',
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
console.log({ value });
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Login or register</h1>
|
||||
<form>
|
||||
<form.Field
|
||||
name="email"
|
||||
children={(field) => (
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<form.Field
|
||||
name="password"
|
||||
children={(field) => (
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div>
|
||||
<button
|
||||
className="button"
|
||||
type="button"
|
||||
onClick={() =>
|
||||
user.login(form.state.values.email, form.state.values.password)
|
||||
}
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
<button
|
||||
className="button"
|
||||
type="button"
|
||||
onClick={() =>
|
||||
user.register(form.state.values.email, form.state.values.password)
|
||||
}
|
||||
>
|
||||
Register
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<button onClick={() => user.loginAsGuest()}>Login as guest</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
||||
Reference in New Issue
Block a user