Skip to content

Commit 3359809

Browse files
authored
Merge pull request #15 from codersforcauses/tasks-page-frontend-upgrade
Tasks page frontend upgrade (Issue #6)
2 parents f17b80b + 6f39058 commit 3359809

16 files changed

Lines changed: 448 additions & 266 deletions

client/package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"next": "15.4.7",
2828
"react": "19.1.0",
2929
"react-dom": "19.1.0",
30+
"react-icons": "^5.5.0",
3031
"tailwind-merge": "^3.3.1",
3132
"tailwindcss-animate": "^1.0.7"
3233
},

client/src/components/task_form.tsx

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,34 +93,39 @@ export function TaskForm({ userId, onTaskCreated }: TaskFormProps) {
9393
return (
9494
<form
9595
onSubmit={handleSubmit}
96-
className="mx-auto h-[79vh] w-full max-w-md space-y-4 overflow-y-auto rounded-xl bg-zinc-700 p-4"
96+
className="task-form flex h-full w-full flex-col items-center justify-between gap-2 rounded-lg bg-slate-400 p-4 text-slate-200"
9797
>
9898
<input
99+
className="title-input w-full rounded-lg bg-slate-400 px-3 py-1 text-3xl font-bold text-slate-100 brightness-90 placeholder:text-slate-300 hover:brightness-110"
99100
type="text"
100101
value={taskName}
101102
onChange={(e) => setTaskName(e.target.value)}
102-
placeholder="Task Name"
103-
className="w-full rounded border-2 bg-zinc-700 p-2 text-zinc-200"
103+
placeholder="Task name"
104104
required
105105
/>
106-
<textarea
107-
value={description}
108-
onChange={(e) => setDescription(e.target.value)}
109-
placeholder="Description"
110-
className="h-32 w-full resize-none rounded border-2 bg-zinc-700 p-2 text-zinc-200"
111-
/>
112-
<TimeInput times={times} setTimes={setTimes} />
106+
<div className="description-wrapper flex w-full flex-col items-center">
107+
<h1 className="description-input-title text-xl hover:brightness-110">
108+
Description
109+
</h1>
110+
<textarea
111+
className="description-input w-full rounded-lg bg-slate-400 p-2 brightness-90 placeholder:text-slate-300 hover:brightness-110"
112+
value={description}
113+
onChange={(e) => setDescription(e.target.value)}
114+
rows={7}
115+
placeholder="Add description here..."
116+
/>
117+
</div>
113118
<TopicInput
114119
availableTopics={availableTopics}
115120
topics={topics}
116121
setTopics={setTopics}
117122
/>
118-
123+
<TimeInput times={times} setTimes={setTimes} />
119124
<button
125+
className="w-fit rounded-lg border border-slate-300 bg-slate-400 px-3 py-1 text-xl brightness-110 hover:brightness-125"
120126
type="submit"
121-
className="rounded border-2 border-zinc-200 bg-blue-600 px-4 py-2 text-zinc-200 hover:bg-blue-700"
122127
>
123-
Add Task
128+
Save
124129
</button>
125130
</form>
126131
);

client/src/components/task_item.tsx

Lines changed: 84 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { useState } from "react";
2+
import { FaRegEdit } from "react-icons/fa";
3+
import { FaRegTrashCan } from "react-icons/fa6";
24

35
import { TimeInput } from "@/components/time_input";
6+
import TimeTag, { DayTag } from "@/components/time_tag";
47
import { TopicInput } from "@/components/topic_input";
8+
import { TopicTagAlt } from "@/components/topic_tag";
59

610
interface Time {
711
id: number;
@@ -122,52 +126,61 @@ export function TaskItem({
122126
/* Item Display */
123127
}
124128
return (
125-
<div className="m-1 flex w-96 flex-col rounded-md border-2 border-zinc-200 bg-zinc-700 p-2">
126-
<div className="flex items-center justify-between">
129+
<div className="task flex h-fit w-full max-w-[48rem] flex-col gap-3 rounded-lg bg-slate-400 p-5 text-slate-200 shadow-xl">
130+
<div className="task-title-container flex w-full flex-row items-center justify-start gap-2 text-3xl font-bold text-slate-100 hover:text-slate-100">
127131
{/* Task Name and Completion */}
128-
<div className="flex items-center space-x-2">
132+
<input
133+
className="task-completion mr-3 accent-slate-300"
134+
type="checkbox"
135+
checked={item.completed}
136+
onChange={() => onToggle?.(item.id)}
137+
/>
138+
{isEditing ? (
129139
<input
130-
type="checkbox"
131-
checked={item.completed}
132-
onChange={() => onToggle?.(item.id)}
133-
className="w-xl h-xl accent-zinc-600"
140+
className="task-title-edit w-full rounded-lg bg-slate-400 px-3 py-1 brightness-90 hover:brightness-110"
141+
value={draftName}
142+
onChange={(e) => setDraftName(e.target.value)}
134143
/>
135-
{isEditing ? (
136-
<input
137-
value={draftName}
138-
onChange={(e) => setDraftName(e.target.value)}
139-
className="rounded border-2 bg-zinc-600 p-1 text-zinc-200"
140-
/>
141-
) : (
144+
) : (
145+
<>
142146
<span
143147
className={
144-
item.completed ? "text-zinc-400 line-through" : "text-zinc-300"
148+
"task-title w-full" + (item.completed ? " line-through" : "")
145149
}
146150
>
147151
{item.name}
148152
</span>
149-
)}
150-
</div>
153+
<button className="hover:brightness-110" onClick={startEdit}>
154+
<FaRegEdit className="h-5 w-5" />
155+
</button>
156+
<button onClick={() => onDelete(item.id)}>
157+
<FaRegTrashCan className="h-5 w-5 hover:text-red-600" />
158+
</button>
159+
</>
160+
)}
161+
</div>
151162

152-
{/* Task Times */}
153-
<div className="flex flex-col items-end text-sm text-zinc-300">
154-
{isEditing ? (
155-
<TimeInput times={draftTimes} setTimes={setDraftTimes} />
156-
) : item.times?.length > 0 ? (
157-
item.times.map((t) => (
158-
<span key={t.id}>
159-
{formatDay(t.day)} {t.start_time.slice(0, 5)} -{" "}
160-
{t.end_time.slice(0, 5)}
161-
</span>
162-
))
163-
) : (
164-
"No time set"
165-
)}
166-
</div>
163+
{/* Task Times */}
164+
<div className="task-times-container flex flex-row flex-wrap items-start justify-start gap-y-1">
165+
{isEditing ? (
166+
<TimeInput times={draftTimes} setTimes={setDraftTimes} />
167+
) : item.times?.length > 0 ? (
168+
item.times.map((time) => (
169+
<div
170+
key={time.id}
171+
className="day-time-tags flex w-fit flex-row gap-3 rounded-full bg-slate-400 px-3 py-1 hover:brightness-110"
172+
>
173+
<DayTag day={formatDay(time.day)} />
174+
<TimeTag start_time={time.start_time} end_time={time.end_time} />
175+
</div>
176+
))
177+
) : (
178+
"No time set"
179+
)}
167180
</div>
168181

169182
{/* Task Topics */}
170-
<div className="flex flex-wrap gap-1 text-sm text-zinc-300">
183+
<div className="task-topic-tags flex flex-row flex-wrap gap-1 text-slate-200">
171184
{isEditing ? (
172185
<TopicInput
173186
availableTopics={availableTopics}
@@ -176,73 +189,69 @@ export function TaskItem({
176189
/>
177190
) : item.topics.length > 0 ? (
178191
item.topics.map((topic) => (
179-
<span
192+
<TopicTagAlt
180193
key={topic.id}
181-
className="flex items-center gap-1 rounded-lg border-2 border-zinc-500 px-2 py-0.5 text-zinc-100"
182-
>
183-
<span
184-
className="h-2 w-2 rounded-full"
185-
style={{
186-
backgroundColor: `#${topic.color_hex
187-
.toString(16)
188-
.padStart(6, "0")}`,
189-
}}
190-
/>
191-
{topic.name}
192-
</span>
194+
name={topic.name}
195+
color_hex={topic.color_hex}
196+
/>
193197
))
194198
) : (
195199
"No topics"
196200
)}
197201
</div>
198202

199203
{/* Task Description */}
200-
<div>
204+
<div
205+
className="task-description overflow-auto text-justify text-slate-200"
206+
style={!isEditing ? { maxHeight: "12rem", scrollbarWidth: "thin" } : {}}
207+
>
201208
{isEditing ? (
202-
<textarea
203-
value={draftDescription}
204-
onChange={(e) => setDraftDescription(e.target.value)}
205-
className="mt-2 w-full rounded border-2 bg-zinc-600 p-1 text-zinc-200"
206-
/>
207-
) : item.description ? (
208-
<span className="mt-2 block text-zinc-300">{item.description}</span>
209+
<div className="task-description-input-container flex flex-col items-center gap-2">
210+
<h1 className="description-input-title text-xl hover:brightness-110">
211+
Description
212+
</h1>
213+
<textarea
214+
className="w-full rounded-lg bg-slate-400 p-1 brightness-90 hover:brightness-110"
215+
value={draftDescription}
216+
rows={7}
217+
onChange={(e) => setDraftDescription(e.target.value)}
218+
/>
219+
</div>
209220
) : (
210-
<span className="mt-2 block italic text-zinc-500">
211-
No description.
221+
<span className="block hover:brightness-110">
222+
{item.description ? item.description : "No Description"}
212223
</span>
213224
)}
214225
</div>
215226

216227
{/* Edit Button */}
217-
<div className="mt-2 flex gap-2">
228+
229+
<div className="task-edit flex gap-2">
218230
{isEditing ? (
219-
<>
220-
<button onClick={saveEdit} disabled={saving}>
231+
<div className="flex w-full flex-row justify-center text-lg">
232+
<button
233+
className="rounded-full bg-slate-400 px-3 py-1 hover:brightness-110"
234+
onClick={saveEdit}
235+
disabled={saving}
236+
>
221237
Save
222238
</button>
223-
<button onClick={cancelEdit}>Cancel</button>
224-
</>
239+
<button
240+
className="rounded-full bg-slate-400 px-3 py-1 hover:brightness-110"
241+
onClick={cancelEdit}
242+
>
243+
Cancel
244+
</button>
245+
</div>
225246
) : (
226-
<button onClick={startEdit}>Edit</button>
227-
)}
228-
</div>
229-
230-
{/* Delete Button */}
231-
<div>
232-
{!isEditing && (
233-
<button
234-
onClick={() => onDelete(item.id)}
235-
className="mt-2 text-red-500"
236-
>
237-
Delete Task
238-
</button>
247+
<></>
239248
)}
240249
</div>
241250
</div>
242251
);
243252
}
244253

245-
function formatDay(day: number): string {
254+
export function formatDay(day: number): string {
246255
const days = [
247256
"Monday",
248257
"Tuesday",

client/src/components/task_list.tsx

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,23 @@ export function TaskList({
4141
if (items.length === 0) {
4242
return (
4343
<div>
44-
<p className="text-center text-zinc-300">No tasks available.</p>
44+
<p className="text-center text-slate-300">No tasks available.</p>
4545
</div>
4646
);
4747
}
4848
return (
49-
<div className="mx-auto h-[79vh] w-full max-w-md overflow-y-auto p-2">
50-
<ul className="space-y-2">
51-
{items.map((item) => (
52-
<li key={item.id}>
53-
<TaskItem
54-
item={item}
55-
onToggle={onToggleTask}
56-
onUpdate={onUpdate}
57-
onDelete={onDelete}
58-
availableTopics={availableTopics}
59-
/>
60-
</li>
61-
))}
62-
</ul>
63-
</div>
49+
<ul className="task-list h-fit space-y-3">
50+
{items.map((item) => (
51+
<li key={item.id}>
52+
<TaskItem
53+
item={item}
54+
onToggle={onToggleTask}
55+
onUpdate={onUpdate}
56+
onDelete={onDelete}
57+
availableTopics={availableTopics}
58+
/>
59+
</li>
60+
))}
61+
</ul>
6462
);
6563
}

0 commit comments

Comments
 (0)