Skip to content

Commit

Permalink
Merge pull request #1652 from acm-ucr/nidheesh-m-vakharia/timer-page
Browse files Browse the repository at this point in the history
Timer Page Implimentation
  • Loading branch information
shahdivyank authored Aug 5, 2024
2 parents 76f1dc4 + 7f6fce6 commit 1e2e434
Show file tree
Hide file tree
Showing 28 changed files with 689 additions and 212 deletions.
2 changes: 1 addition & 1 deletion components.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/app/globals.css",
Expand Down
47 changes: 47 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@react-email/components": "^0.0.16",
Expand All @@ -36,6 +37,7 @@
"firebase": "^10.8.0",
"firebase-admin": "^11.11.0",
"gray-matter": "^4.0.3",
"input-otp": "^1.2.4",
"jszip": "^3.10.1",
"lucide-react": "^0.408.0",
"next": "^14.0.1",
Expand Down Expand Up @@ -64,6 +66,7 @@
"@types/node": "^20.14.10",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/uuid": "^10.0.0",
"autoprefixer": "^10.4.16",
"cypress": "^13.12.0",
"eslint": "^8.53.0",
Expand Down
2 changes: 2 additions & 0 deletions src/app/admin/[type]/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Volunteers from "@/components/admin/dashboards/Volunteers";
import Leads from "@/components/admin/dashboards/Leads";
import Fault from "@/utils/error";
import Settings from "@/components/admin/services/settings/Settings";
import Timer from "@/components/admin/services/timer/Timer";

const Page = ({ params, searchParams }) => {
const components = {
Expand All @@ -40,6 +41,7 @@ const Page = ({ params, searchParams }) => {
teams: <Teams searchParams={searchParams} />,
volunteers: <Volunteers searchParams={searchParams} />,
leads: <Leads searchParams={searchParams} />,
timer: <Timer searchParams={searchParams} />,
};

const capitalizeFirstLetter = (word) => {
Expand Down
111 changes: 111 additions & 0 deletions src/components/admin/services/timer/Clock.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { useState, useEffect } from "react";
import Controls from "./Controls";
import { Progress } from "@/components/ui/progress";
import { Pause, Play } from "lucide-react";
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp";

const Timer = ({ onRemove }) => {
const [edit, setEdit] = useState(false);
const [play, setPlay] = useState(false);
const [total, setTotal] = useState(60000);
const [original, setOriginal] = useState(0);
const [time, setTime] = useState({
hours: "00",
minutes: "01",
seconds: "00",
});

const onChange = (value) => {
if (!edit) return;

const [hours, minutes, seconds] = value.match(/.{2}/g);

setTime({
hours,
minutes,
seconds,
});

setTotal(
parseInt(hours) * (1000 * 60 * 60) +
parseInt(minutes) * (1000 * 60) +
parseInt(seconds) * 1000,
);
};

useEffect(() => {
const id = setInterval(() => {
if (!play) return;

const newSeconds = total - 1000;

setTotal((prev) => prev - 1000);

setTime({
hours: Math.floor((newSeconds / (1000 * 60 * 60)) % 24)
.toString()
.padStart(2, "0"),
minutes: Math.floor((newSeconds / 1000 / 60) % 60)
.toString()
.padStart(2, "0"),
seconds: Math.floor((newSeconds / 1000) % 60)
.toString()
.padStart(2, "0"),
});

setOriginal((prev) => prev + 1000);
}, 1000);

return () => clearInterval(id);
}, [play, total]);

return (
<div className="mb-4 flex scroll-m-4 flex-col items-center justify-between rounded-xl bg-white p-4">
<div className="flex w-full items-center justify-between">
<input
disabled={!edit}
className="flex-grow bg-transparent pl-2 text-3xl font-semibold outline-none"
placeholder="Untitled Timer"
/>
<Controls edit={edit} setEdit={setEdit} onRemove={onRemove} />
</div>

<InputOTP
disabled={!edit}
maxLength={6}
onChange={onChange}
value={time["hours"] + time["minutes"] + time["seconds"]}
className="mt-4"
>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
</InputOTPGroup>
<InputOTPGroup>
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
</InputOTPGroup>
<InputOTPGroup>
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>

<div className="mt-4">
{play && !edit && <Pause onClick={() => setPlay(false)} />}
{!play && !edit && <Play onClick={() => setPlay(true)} />}
</div>

<Progress
value={(total / (original + total)) * 100}
className="mt-4 w-full"
/>
</div>
);
};

export default Timer;
28 changes: 28 additions & 0 deletions src/components/admin/services/timer/Controls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Trash2, Pen, Check } from "lucide-react";

type props = {
edit: boolean;
setEdit: (value: boolean) => void;
onRemove: () => void;
};

const Controls = ({ edit, setEdit, onRemove }: props) => {
return (
<div className="flex gap-4">
{edit && (
<>
<Check onClick={() => setEdit(false)} />
<Trash2 onClick={onRemove} />
</>
)}

{!edit && (
<>
<Pen onClick={() => setEdit(true)} />
<Trash2 onClick={onRemove} />
</>
)}
</div>
);
};
export default Controls;
55 changes: 55 additions & 0 deletions src/components/admin/services/timer/Timer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useState } from "react";
import Clock from "./Clock";
import { v4 as uuidv4 } from "uuid";
import Title from "../../Title";
import { Button } from "@/components/ui/button";

type TimerType = {
id: string;
};

const Timer = () => {
const [timers, setTimers] = useState<TimerType[]>([]);

const addTimer = () => {
setTimers([
...timers,
{
id: uuidv4(),
},
]);
};

const clearAll = () => {
setTimers([]);
};

const deleteTimer = (id: string) => {
setTimers(timers.filter((timer) => timer.id !== id));
};

return (
<div className="flex h-full flex-col py-4 font-poppins">
<div className="mb-4 flex gap-3">
<Title title="Timer" />
<Button onClick={addTimer}>+ add timer</Button>
<Button variant="destructive" onClick={clearAll}>
clear all
</Button>
</div>
<div className="flex h-full flex-col overflow-y-scroll rounded-3xl bg-gray-200 p-4">
{timers.length === 0 ? (
<div className="flex h-full items-center justify-center text-2xl font-bold opacity-30">
No Timers
</div>
) : (
timers.map((timer) => (
<Clock key={timer.id} onRemove={() => deleteTimer(timer.id)} />
))
)}
</div>
</div>
);
};

export default Timer;
Loading

0 comments on commit 1e2e434

Please sign in to comment.