From bce6a98258e1f3024c74cbf4a1c320d5dca8c9cf Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Tue, 30 Jan 2024 23:17:06 -0800 Subject: [PATCH 01/23] feat: implement CSS for Figma design --- .../src/components/EditProgram.module.css | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 frontend/src/components/EditProgram.module.css diff --git a/frontend/src/components/EditProgram.module.css b/frontend/src/components/EditProgram.module.css new file mode 100644 index 00000000..af5a71a3 --- /dev/null +++ b/frontend/src/components/EditProgram.module.css @@ -0,0 +1,216 @@ +.form { + width: 872px; + height: 562px; + + border-radius: 8px; + background: var(--Primary-PIA---primary-white, #FFF); +} + +.form-title { + color: var(--Primary-PIA---primary-text, #202124); + + /* Title/Title - Small */ + font-family: Poppins; + font-size: 24px; + font-style: normal; + font-weight: 700; + line-height: 150%; /* 36px */ + letter-spacing: 0.48px; +} + +.form-container { + display: inline-flex; + flex-direction: column; + align-items: flex-start; + gap: 24px; +} + +.program-name-label { + color: var(--Neutral-Gray4, #909090); + text-align: center; + + /* Body/Medium (Regular) */ + font-family: Poppins; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 150%; /* 24px */ +} + +.program-name-input { + color: var(--Primary-PIA---primary-text, #202124); + + /* Body/Medium (Regular) */ + font-family: Poppins; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 150%; /* 24px */ +} + +.program-abbr-label { + color: var(--Neutral-Gray4, #909090); + + /* Body/Medium (Regular) */ + font-family: Poppins; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 150%; /* 24px */ +} + +.program-abbr-input { + color: var(--Primary-PIA---primary-text, #202124); + + /* Body/Medium (Regular) */ + font-family: Poppins; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 150%; /* 24px */ +} + +.program-type-label { + color: var(--Neutral-Gray4, #909090); + text-align: center; + + /* Body/Medium (Regular) */ + font-family: Poppins; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 150%; /* 24px */ +} + +.program-type-selected { + display: flex; + width: 144px; + height: 48px; + padding: 12px 24px; + justify-content: center; + align-items: center; + gap: 6px; + + border-radius: 0px 4px 4px 0px; + border-top: 1px solid var(--Neutral-Gray4, #909090); + border-right: 1px solid var(--Neutral-Gray4, #909090); + border-bottom: 1px solid var(--Neutral-Gray4, #909090); + background: rgba(255, 255, 255, 0.00); +} + +.program-type-unselected { + display: flex; + width: 144px; + height: 48px; + padding: 12px 24px; + justify-content: center; + align-items: center; + gap: 6px; + + border-radius: 0px 4px 4px 0px; + border-top: 1px solid var(--Neutral-Gray4, #909090); + border-right: 1px solid var(--Neutral-Gray4, #909090); + border-bottom: 1px solid var(--Neutral-Gray4, #909090); + background: rgba(255, 255, 255, 0.00); +} + +.program-date-label { + color: var(--Neutral-Gray4, #909090); + + /* Body/Medium (Regular) */ + font-family: Poppins; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 150%; /* 24px */ +} + +.program-date-input { + display: inline-flex; + padding: 8px 16px; + align-items: center; + gap: 10px; + + border-radius: 4px; + border: 1px solid var(--Neutral-Gray4, #909090); + background: var(--Neutral-Gray0, #F3F3F3); +} + +.program-date-icon { + width: 20px; + height: 20px; +} + +.program-date-date { + flex: 1 0 0; + + color: var(--Primary-PIA---primary-text, #202124); + + /* Body/Medium (Regular) */ + font-family: Poppins; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 150%; /* 24px */ +} + +.program-color-container { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; +} + +.program-color-label { + color: var(--Neutral-Gray4, #909090); + text-align: center; + + /* Body/Medium (Regular) */ + font-family: Poppins; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 150%; /* 24px */ +} + +.program-color-options-container { + display: flex; + align-items: flex-start; + gap: 16px; +} + +.program-color-option-teal { + width: 40px; + height: 40px; + + fill: var(--secondary-pia-secondary-teal-4-fa-197, #4FA197); + stroke-width: 1px; + stroke: var(--Neutral-Gray4, #909090); +} + +.program-color-option-yellow { + width: 40px; + height: 40px; + + fill: var(--secondary-pia-secondary-yellow-ffb-800, #FFB800); + stroke-width: 1px; + stroke: var(--Neutral-Gray4, #909090); +} + +.program-color-option-red { + width: 40px; + height: 40px; + + fill: var(--secondary-pia-secondary-red-ff-7-a-5-e, #FF7A5E); + stroke-width: 1px; + stroke: var(--Neutral-Gray4, #909090); +} + +.program-color-option-green { + width: 40px; + height: 40px; + + fill: var(--secondary-pia-secondary-green-b-6-bf-0-e, #B6BF0E); + stroke-width: 1px; + stroke: var(--Neutral-Gray4, #909090); +} \ No newline at end of file From 473c8e54a640187957d3e36a5db628535bbeaf3a Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Tue, 30 Jan 2024 23:27:19 -0800 Subject: [PATCH 02/23] feat: create src/api/programs.ts to handle program data --- frontend/src/api/programs.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 frontend/src/api/programs.ts diff --git a/frontend/src/api/programs.ts b/frontend/src/api/programs.ts new file mode 100644 index 00000000..d2e5d4b6 --- /dev/null +++ b/frontend/src/api/programs.ts @@ -0,0 +1,31 @@ +/** + * + */ + + +/** + * + */ +export interface Program { + _id: string; + name: string; + abbr: string; + type: "regular" | "varying"; + start: Date; + end: Date; + color: "teal" | "yellow" | "red" | "green"; +} + + +/** + * + */ +export interface ProgramJSON { + _id: string; + name: string; + abbr: string; + type: "regular" | "varying"; + start: string; + end: string; + color: "teal" | "yellow" | "red" | "green"; +} From 83e14ffacf288dda9f05c65dac2a7d3fc14e9e3d Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Tue, 30 Jan 2024 23:30:30 -0800 Subject: [PATCH 03/23] feat: create parseProgram() function to parse program JSON data --- frontend/src/api/programs.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frontend/src/api/programs.ts b/frontend/src/api/programs.ts index d2e5d4b6..f0d0acb8 100644 --- a/frontend/src/api/programs.ts +++ b/frontend/src/api/programs.ts @@ -29,3 +29,21 @@ export interface ProgramJSON { end: string; color: "teal" | "yellow" | "red" | "green"; } + + +/** + * + * @param program + * @returns + */ +function parseProgram(program: ProgramJSON): Program { + return { + _id: program._id, + name: program.name, + abbr: program.abbr, + type: program.type, + start: new Date(program.type), + end: new Date(program.type), + color: program.color + }; +} From 6532990cf35dd9e511e169d4ec40fd4c7d0baa94 Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Tue, 30 Jan 2024 23:37:21 -0800 Subject: [PATCH 04/23] feat: create functions to handle program creation/updates --- frontend/src/api/programs.ts | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/frontend/src/api/programs.ts b/frontend/src/api/programs.ts index f0d0acb8..f3ccf29e 100644 --- a/frontend/src/api/programs.ts +++ b/frontend/src/api/programs.ts @@ -2,6 +2,8 @@ * */ +import { type APIResult, GET, handleAPIError, POST } from "src/api/requests"; + /** * @@ -47,3 +49,48 @@ function parseProgram(program: ProgramJSON): Program { color: program.color }; } + + +/** + * + */ +export interface ProgramRequest { + name: string; + abbr: string; + type: "regular" | "varying"; + start: Date; + end: Date; + color: "teal" | "yellow" | "red" | "green"; +} + + +/** + * + * @param program + * @returns + */ +export async function createProgram(program: ProgramRequest): Promise> { + try { + const response = await POST("/api/program", program); + const json = (await response.json()) as ProgramJSON; + return { success: true, data: parseProgram(json) }; + } catch (error) { + handleAPIError(error); + } +} + + +/** + * + * @param id + * @returns + */ +export async function getProgram(id: string): Promise> { + try { + const response = await GET(`/api/program/${id}`); + const json = (await response.json()) as ProgramJSON; + return { success: true, data: parseProgram(json) }; + } catch (error) { + return handleAPIError(error); + } +} From 587aa8ae3622c4c0a6f447bb6d65e4fa0e3e637d Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Tue, 30 Jan 2024 23:38:04 -0800 Subject: [PATCH 05/23] fix: correct ProgramForm.module.css path --- .../components/{EditProgram.module.css => ProgramForm.module.css} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename frontend/src/components/{EditProgram.module.css => ProgramForm.module.css} (100%) diff --git a/frontend/src/components/EditProgram.module.css b/frontend/src/components/ProgramForm.module.css similarity index 100% rename from frontend/src/components/EditProgram.module.css rename to frontend/src/components/ProgramForm.module.css From 8c588091f29f5714d241db5ab2408fb310470197 Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Tue, 30 Jan 2024 23:50:11 -0800 Subject: [PATCH 06/23] feat: create frontend/src/components/ProgramForm.tsx --- frontend/src/components/ProgramForm.tsx | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 frontend/src/components/ProgramForm.tsx diff --git a/frontend/src/components/ProgramForm.tsx b/frontend/src/components/ProgramForm.tsx new file mode 100644 index 00000000..a1f31ca0 --- /dev/null +++ b/frontend/src/components/ProgramForm.tsx @@ -0,0 +1,56 @@ +import React, { useState } from "react"; +import { createProgram, type Program } from "src/api/programs"; +import styles from "src/components/EditProgram.module.css"; + + +/** + * + */ +export interface ProgramFormProperties { + mode: "create" | "edit"; + program?: Program; + onSubmit?: (program: Program) => void; +} + + +/** + * + */ +interface ProgramFormErrors { + name?: boolean; + abbr?: boolean; + type?: boolean; + start?: boolean; + end?: boolean; + color?: boolean; +} + + +export function ProgramForm({ mode, program, onSubmit }: ProgramFormProperties) { + const [name, setName] = useState(program?.title || ""); + const [abbr, setAbbr] = useState(program?.abbr || ""); + const [type, setType] = useState(program?.type || ""); + const [start, setStart] = useState(program?.start || ""); + const [end, setEnd] = useState(program?.end || ""); + const [color, setColor] = useState(program?.color || ""); + + const [isLoading, setLoading] = useState(false); + const [errors, setErrors] = useState({}); + + const handleSubmit = () => { + setErrors({}); + + // TODO: Input validation + + setLoading(true); + createTask({ name, abbr, type, start, end, color }).then((result) => { + setLoading(false); + }); + } + + const formTitle = (mode === "create" ? "Add New Program" : name); + + return ( +
+ ); +} \ No newline at end of file From da73ada67b23ccb525eede06e652eff9cb57146b Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Tue, 30 Jan 2024 23:59:08 -0800 Subject: [PATCH 07/23] feat: fill Promise.then() callback for createProgram() --- frontend/src/components/ProgramForm.tsx | 31 ++++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/ProgramForm.tsx b/frontend/src/components/ProgramForm.tsx index a1f31ca0..2941bfb0 100644 --- a/frontend/src/components/ProgramForm.tsx +++ b/frontend/src/components/ProgramForm.tsx @@ -19,10 +19,6 @@ export interface ProgramFormProperties { interface ProgramFormErrors { name?: boolean; abbr?: boolean; - type?: boolean; - start?: boolean; - end?: boolean; - color?: boolean; } @@ -38,12 +34,35 @@ export function ProgramForm({ mode, program, onSubmit }: ProgramFormProperties) const [errors, setErrors] = useState({}); const handleSubmit = () => { + // Preliminary input validation setErrors({}); - - // TODO: Input validation + if (name.length === 0) { + setErrors({ name: true }); + return; + } + if (abbr.length === 0) { + setErrors({ abbr: true }); + return; + } setLoading(true); createTask({ name, abbr, type, start, end, color }).then((result) => { + if (result.success) { + // Clear form + setName(""); + setAbbr(""); + setType("regular"); + setStart(new Date()); + setEnd(new Date()); + setColor("teal"); + + if (onSubmit) { + onSubmit(result.data); + } + } else { + alert(result.error); + } + setLoading(false); }); } From e01e21dbde61ff3f52ba8230e49d9f4c239815ce Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Wed, 31 Jan 2024 00:02:59 -0800 Subject: [PATCH 08/23] fix: rename CSS class names --- .../src/components/ProgramForm.module.css | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/frontend/src/components/ProgramForm.module.css b/frontend/src/components/ProgramForm.module.css index af5a71a3..109e33ee 100644 --- a/frontend/src/components/ProgramForm.module.css +++ b/frontend/src/components/ProgramForm.module.css @@ -6,7 +6,7 @@ background: var(--Primary-PIA---primary-white, #FFF); } -.form-title { +.formTitle { color: var(--Primary-PIA---primary-text, #202124); /* Title/Title - Small */ @@ -18,14 +18,14 @@ letter-spacing: 0.48px; } -.form-container { +.formContainer { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 24px; } -.program-name-label { +.programNameLabel { color: var(--Neutral-Gray4, #909090); text-align: center; @@ -37,7 +37,7 @@ line-height: 150%; /* 24px */ } -.program-name-input { +.programNameInput { color: var(--Primary-PIA---primary-text, #202124); /* Body/Medium (Regular) */ @@ -48,7 +48,7 @@ line-height: 150%; /* 24px */ } -.program-abbr-label { +.programAbbrLabel { color: var(--Neutral-Gray4, #909090); /* Body/Medium (Regular) */ @@ -59,7 +59,7 @@ line-height: 150%; /* 24px */ } -.program-abbr-input { +.programAbbrInput { color: var(--Primary-PIA---primary-text, #202124); /* Body/Medium (Regular) */ @@ -70,7 +70,7 @@ line-height: 150%; /* 24px */ } -.program-type-label { +.programTypeLabel { color: var(--Neutral-Gray4, #909090); text-align: center; @@ -82,7 +82,7 @@ line-height: 150%; /* 24px */ } -.program-type-selected { +.programTypeSelected { display: flex; width: 144px; height: 48px; @@ -98,7 +98,7 @@ background: rgba(255, 255, 255, 0.00); } -.program-type-unselected { +.programTypeUnselected { display: flex; width: 144px; height: 48px; @@ -114,7 +114,7 @@ background: rgba(255, 255, 255, 0.00); } -.program-date-label { +.programDateLabel { color: var(--Neutral-Gray4, #909090); /* Body/Medium (Regular) */ @@ -125,7 +125,7 @@ line-height: 150%; /* 24px */ } -.program-date-input { +.programDateInput { display: inline-flex; padding: 8px 16px; align-items: center; @@ -136,12 +136,12 @@ background: var(--Neutral-Gray0, #F3F3F3); } -.program-date-icon { +.programDateIcon { width: 20px; height: 20px; } -.program-date-date { +.programDateDate { flex: 1 0 0; color: var(--Primary-PIA---primary-text, #202124); @@ -154,14 +154,14 @@ line-height: 150%; /* 24px */ } -.program-color-container { +.programColorContainer { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; } -.program-color-label { +.programColorLabel { color: var(--Neutral-Gray4, #909090); text-align: center; @@ -173,13 +173,13 @@ line-height: 150%; /* 24px */ } -.program-color-options-container { +.programColorOptionsContainer { display: flex; align-items: flex-start; gap: 16px; } -.program-color-option-teal { +.programColorOptionTeal { width: 40px; height: 40px; @@ -188,7 +188,7 @@ stroke: var(--Neutral-Gray4, #909090); } -.program-color-option-yellow { +.programColorOptionYellow { width: 40px; height: 40px; @@ -197,7 +197,7 @@ stroke: var(--Neutral-Gray4, #909090); } -.program-color-option-red { +.programColorOptionRed { width: 40px; height: 40px; @@ -206,7 +206,7 @@ stroke: var(--Neutral-Gray4, #909090); } -.program-color-option-green { +.programColorOptionGreen { width: 40px; height: 40px; From 1f006308cb97e883fa6fd03180ded13d12a59fae Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Wed, 14 Feb 2024 09:58:06 -0800 Subject: [PATCH 09/23] fix: update frontend/src/api/program.ts --- frontend/src/api/{programs.ts => program.ts} | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) rename frontend/src/api/{programs.ts => program.ts} (80%) diff --git a/frontend/src/api/programs.ts b/frontend/src/api/program.ts similarity index 80% rename from frontend/src/api/programs.ts rename to frontend/src/api/program.ts index f3ccf29e..92d5f650 100644 --- a/frontend/src/api/programs.ts +++ b/frontend/src/api/program.ts @@ -2,34 +2,34 @@ * */ -import { type APIResult, GET, handleAPIError, POST } from "src/api/requests"; +import { APIResult, GET, POST, handleAPIError } from "./requests"; /** * */ -export interface Program { +export type Program = { _id: string; name: string; abbr: string; type: "regular" | "varying"; start: Date; end: Date; - color: "teal" | "yellow" | "red" | "green"; -} + color: string; +}; /** * */ -export interface ProgramJSON { +export type ProgramJSON = { _id: string; name: string; abbr: string; type: "regular" | "varying"; start: string; end: string; - color: "teal" | "yellow" | "red" | "green"; + color: string; } @@ -54,13 +54,13 @@ function parseProgram(program: ProgramJSON): Program { /** * */ -export interface ProgramRequest { +export type ProgramRequest = { name: string; abbr: string; type: "regular" | "varying"; start: Date; end: Date; - color: "teal" | "yellow" | "red" | "green"; + color: string; } @@ -75,7 +75,7 @@ export async function createProgram(program: ProgramRequest): Promise> { +export async function getProgram(id: string): Promise> { try { const response = await GET(`/api/program/${id}`); const json = (await response.json()) as ProgramJSON; From 76d2df0cfa8a31b5ca447d05cc6f3cca61985eb8 Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Fri, 16 Feb 2024 11:54:31 -0800 Subject: [PATCH 10/23] feat: update program.ts --- frontend/src/api/program.ts | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/frontend/src/api/program.ts b/frontend/src/api/program.ts index 92d5f650..4e1c6206 100644 --- a/frontend/src/api/program.ts +++ b/frontend/src/api/program.ts @@ -5,16 +5,23 @@ import { APIResult, GET, POST, handleAPIError } from "./requests"; +/** + * + */ +export type Day = "M" | "T" | "W" | "TH" | "F" + + /** * */ export type Program = { _id: string; name: string; - abbr: string; + abbreviation: string; type: "regular" | "varying"; - start: Date; - end: Date; + days: Day[]; + start: string; + end: string; color: string; }; @@ -25,8 +32,9 @@ export type Program = { export type ProgramJSON = { _id: string; name: string; - abbr: string; + abbreviation: string; type: "regular" | "varying"; + days: Day[]; start: string; end: string; color: string; @@ -42,10 +50,11 @@ function parseProgram(program: ProgramJSON): Program { return { _id: program._id, name: program.name, - abbr: program.abbr, + abbreviation: program.abbreviation, type: program.type, - start: new Date(program.type), - end: new Date(program.type), + days: program.days, + start: program.start, + end: program.end, color: program.color }; } @@ -56,8 +65,9 @@ function parseProgram(program: ProgramJSON): Program { */ export type ProgramRequest = { name: string; - abbr: string; + abbreviation: string; type: "regular" | "varying"; + days: Day[]; start: Date; end: Date; color: string; From 018899c6cf2d793df024bd5ba4658896a4ba542e Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Fri, 16 Feb 2024 12:33:13 -0800 Subject: [PATCH 11/23] feat: update ProgramForm.tsx --- frontend/src/components/ProgramForm.tsx | 103 ++++++++++++++---------- 1 file changed, 62 insertions(+), 41 deletions(-) diff --git a/frontend/src/components/ProgramForm.tsx b/frontend/src/components/ProgramForm.tsx index 2941bfb0..07c3e599 100644 --- a/frontend/src/components/ProgramForm.tsx +++ b/frontend/src/components/ProgramForm.tsx @@ -1,12 +1,17 @@ import React, { useState } from "react"; -import { createProgram, type Program } from "src/api/programs"; -import styles from "src/components/EditProgram.module.css"; + +import { Day, Program, createProgram } from "../api/program" +import { handleAPIError } from "../api/requests"; + +import styles from "./ProgramForm.module.css"; +import { Textfield } from "./Textfield"; + /** * */ -export interface ProgramFormProperties { +export type ProgramFormProperties = { mode: "create" | "edit"; program?: Program; onSubmit?: (program: Program) => void; @@ -16,60 +21,76 @@ export interface ProgramFormProperties { /** * */ -interface ProgramFormErrors { +type ProgramFormErrors = { name?: boolean; - abbr?: boolean; + abbreviation?: boolean; + type?: boolean; + days?: boolean; + start?: boolean; + end?: boolean; + color?: boolean; } export function ProgramForm({ mode, program, onSubmit }: ProgramFormProperties) { - const [name, setName] = useState(program?.title || ""); - const [abbr, setAbbr] = useState(program?.abbr || ""); - const [type, setType] = useState(program?.type || ""); - const [start, setStart] = useState(program?.start || ""); - const [end, setEnd] = useState(program?.end || ""); - const [color, setColor] = useState(program?.color || ""); + const [name, setName] = useState(program?.name ?? ""); + const [abbreviation, setAbbreviation] = useState(program?.abbreviation ?? ""); + const [type, setType] = useState(program?.type ?? ""); + const [days, setDays] = useState(program?.days ?? []); + const [start, setStart] = useState(program?.start ?? ""); + const [end, setEnd] = useState(program?.end ?? ""); + const [color, setColor] = useState(program?.color ?? ""); const [isLoading, setLoading] = useState(false); const [errors, setErrors] = useState({}); const handleSubmit = () => { - // Preliminary input validation - setErrors({}); - if (name.length === 0) { - setErrors({ name: true }); - return; - } - if (abbr.length === 0) { - setErrors({ abbr: true }); - return; - } + if (name.length === 0) { setErrors({ name: true })}; + if (abbreviation.length === 0) { setErrors({ abbreviation: true })}; + if (type.length === 0) { setErrors({ type: true })}; + if (days.length === 0) { setErrors({ days: true })}; + if (start.length === 0) { setErrors({ start: true })}; + if (end.length === 0) { setErrors({ end: true })}; + if (color.length === 0) { setErrors({ color: true })}; + if ( + errors.name ?? errors.abbreviation ?? errors.type ?? errors.days ?? errors.start + ?? errors.end ?? errors.color + ) { return; } setLoading(true); - createTask({ name, abbr, type, start, end, color }).then((result) => { - if (result.success) { - // Clear form - setName(""); - setAbbr(""); - setType("regular"); - setStart(new Date()); - setEnd(new Date()); - setColor("teal"); - - if (onSubmit) { - onSubmit(result.data); + + createProgram({ name, abbreviation, type, days, start, end, color }).catch( + err => handleAPIError(err) + ).then( + (result) => { + if (result.success) { + setName(""); + setAbbreviation(""); + setType(""); + setDays([]); + setStart(""); + setEnd(""); + setColor(""); + + onSubmit && onSubmit(result.data); + } else { + alert(result.error); } - } else { - alert(result.error); + + setLoading(false); } - - setLoading(false); - }); - } + ); - const formTitle = (mode === "create" ? "Add New Program" : name); + }; + + const formTitle = (mode === "create" ? "Add New Program" : program?.name); return ( -
+
+ {formTitle} +
+ +
+
); } \ No newline at end of file From 3061139c2aa0597e8a1f45f5a73e2f4cd8126d4e Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Fri, 16 Feb 2024 13:42:39 -0800 Subject: [PATCH 12/23] feat: create program add/edit form --- frontend/package-lock.json | 2 +- .../src/components/ProgramForm.module.css | 216 ------------------ frontend/src/components/ProgramForm.tsx | 96 -------- .../src/components/ProgramForm/Program.tsx | 47 ++++ frontend/src/components/ProgramForm/types.ts | 17 ++ frontend/src/components/ProgramFormButton.tsx | 79 +++++++ frontend/src/pages/index.tsx | 6 + frontend/src/sampleProgramData.json | 9 + 8 files changed, 159 insertions(+), 313 deletions(-) delete mode 100644 frontend/src/components/ProgramForm.module.css delete mode 100644 frontend/src/components/ProgramForm.tsx create mode 100644 frontend/src/components/ProgramForm/Program.tsx create mode 100644 frontend/src/components/ProgramForm/types.ts create mode 100644 frontend/src/components/ProgramFormButton.tsx create mode 100644 frontend/src/sampleProgramData.json diff --git a/frontend/package-lock.json b/frontend/package-lock.json index edee9ab2..5790dfc2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -25,7 +25,7 @@ "react-day-picker": "^8.10.0", "react-dom": "^18", "react-hook-form": "^7.49.3", - "tailwind-merge": "^2.2.1", + "tailwind-merge": "^2.2.0", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", "ts-jest": "^29.1.1" diff --git a/frontend/src/components/ProgramForm.module.css b/frontend/src/components/ProgramForm.module.css deleted file mode 100644 index 109e33ee..00000000 --- a/frontend/src/components/ProgramForm.module.css +++ /dev/null @@ -1,216 +0,0 @@ -.form { - width: 872px; - height: 562px; - - border-radius: 8px; - background: var(--Primary-PIA---primary-white, #FFF); -} - -.formTitle { - color: var(--Primary-PIA---primary-text, #202124); - - /* Title/Title - Small */ - font-family: Poppins; - font-size: 24px; - font-style: normal; - font-weight: 700; - line-height: 150%; /* 36px */ - letter-spacing: 0.48px; -} - -.formContainer { - display: inline-flex; - flex-direction: column; - align-items: flex-start; - gap: 24px; -} - -.programNameLabel { - color: var(--Neutral-Gray4, #909090); - text-align: center; - - /* Body/Medium (Regular) */ - font-family: Poppins; - font-size: 16px; - font-style: normal; - font-weight: 400; - line-height: 150%; /* 24px */ -} - -.programNameInput { - color: var(--Primary-PIA---primary-text, #202124); - - /* Body/Medium (Regular) */ - font-family: Poppins; - font-size: 16px; - font-style: normal; - font-weight: 400; - line-height: 150%; /* 24px */ -} - -.programAbbrLabel { - color: var(--Neutral-Gray4, #909090); - - /* Body/Medium (Regular) */ - font-family: Poppins; - font-size: 16px; - font-style: normal; - font-weight: 400; - line-height: 150%; /* 24px */ -} - -.programAbbrInput { - color: var(--Primary-PIA---primary-text, #202124); - - /* Body/Medium (Regular) */ - font-family: Poppins; - font-size: 16px; - font-style: normal; - font-weight: 400; - line-height: 150%; /* 24px */ -} - -.programTypeLabel { - color: var(--Neutral-Gray4, #909090); - text-align: center; - - /* Body/Medium (Regular) */ - font-family: Poppins; - font-size: 16px; - font-style: normal; - font-weight: 400; - line-height: 150%; /* 24px */ -} - -.programTypeSelected { - display: flex; - width: 144px; - height: 48px; - padding: 12px 24px; - justify-content: center; - align-items: center; - gap: 6px; - - border-radius: 0px 4px 4px 0px; - border-top: 1px solid var(--Neutral-Gray4, #909090); - border-right: 1px solid var(--Neutral-Gray4, #909090); - border-bottom: 1px solid var(--Neutral-Gray4, #909090); - background: rgba(255, 255, 255, 0.00); -} - -.programTypeUnselected { - display: flex; - width: 144px; - height: 48px; - padding: 12px 24px; - justify-content: center; - align-items: center; - gap: 6px; - - border-radius: 0px 4px 4px 0px; - border-top: 1px solid var(--Neutral-Gray4, #909090); - border-right: 1px solid var(--Neutral-Gray4, #909090); - border-bottom: 1px solid var(--Neutral-Gray4, #909090); - background: rgba(255, 255, 255, 0.00); -} - -.programDateLabel { - color: var(--Neutral-Gray4, #909090); - - /* Body/Medium (Regular) */ - font-family: Poppins; - font-size: 16px; - font-style: normal; - font-weight: 400; - line-height: 150%; /* 24px */ -} - -.programDateInput { - display: inline-flex; - padding: 8px 16px; - align-items: center; - gap: 10px; - - border-radius: 4px; - border: 1px solid var(--Neutral-Gray4, #909090); - background: var(--Neutral-Gray0, #F3F3F3); -} - -.programDateIcon { - width: 20px; - height: 20px; -} - -.programDateDate { - flex: 1 0 0; - - color: var(--Primary-PIA---primary-text, #202124); - - /* Body/Medium (Regular) */ - font-family: Poppins; - font-size: 16px; - font-style: normal; - font-weight: 400; - line-height: 150%; /* 24px */ -} - -.programColorContainer { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 8px; -} - -.programColorLabel { - color: var(--Neutral-Gray4, #909090); - text-align: center; - - /* Body/Medium (Regular) */ - font-family: Poppins; - font-size: 16px; - font-style: normal; - font-weight: 400; - line-height: 150%; /* 24px */ -} - -.programColorOptionsContainer { - display: flex; - align-items: flex-start; - gap: 16px; -} - -.programColorOptionTeal { - width: 40px; - height: 40px; - - fill: var(--secondary-pia-secondary-teal-4-fa-197, #4FA197); - stroke-width: 1px; - stroke: var(--Neutral-Gray4, #909090); -} - -.programColorOptionYellow { - width: 40px; - height: 40px; - - fill: var(--secondary-pia-secondary-yellow-ffb-800, #FFB800); - stroke-width: 1px; - stroke: var(--Neutral-Gray4, #909090); -} - -.programColorOptionRed { - width: 40px; - height: 40px; - - fill: var(--secondary-pia-secondary-red-ff-7-a-5-e, #FF7A5E); - stroke-width: 1px; - stroke: var(--Neutral-Gray4, #909090); -} - -.programColorOptionGreen { - width: 40px; - height: 40px; - - fill: var(--secondary-pia-secondary-green-b-6-bf-0-e, #B6BF0E); - stroke-width: 1px; - stroke: var(--Neutral-Gray4, #909090); -} \ No newline at end of file diff --git a/frontend/src/components/ProgramForm.tsx b/frontend/src/components/ProgramForm.tsx deleted file mode 100644 index 07c3e599..00000000 --- a/frontend/src/components/ProgramForm.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, { useState } from "react"; - -import { Day, Program, createProgram } from "../api/program" -import { handleAPIError } from "../api/requests"; - -import styles from "./ProgramForm.module.css"; -import { Textfield } from "./Textfield"; - - - -/** - * - */ -export type ProgramFormProperties = { - mode: "create" | "edit"; - program?: Program; - onSubmit?: (program: Program) => void; -} - - -/** - * - */ -type ProgramFormErrors = { - name?: boolean; - abbreviation?: boolean; - type?: boolean; - days?: boolean; - start?: boolean; - end?: boolean; - color?: boolean; -} - - -export function ProgramForm({ mode, program, onSubmit }: ProgramFormProperties) { - const [name, setName] = useState(program?.name ?? ""); - const [abbreviation, setAbbreviation] = useState(program?.abbreviation ?? ""); - const [type, setType] = useState(program?.type ?? ""); - const [days, setDays] = useState(program?.days ?? []); - const [start, setStart] = useState(program?.start ?? ""); - const [end, setEnd] = useState(program?.end ?? ""); - const [color, setColor] = useState(program?.color ?? ""); - - const [isLoading, setLoading] = useState(false); - const [errors, setErrors] = useState({}); - - const handleSubmit = () => { - if (name.length === 0) { setErrors({ name: true })}; - if (abbreviation.length === 0) { setErrors({ abbreviation: true })}; - if (type.length === 0) { setErrors({ type: true })}; - if (days.length === 0) { setErrors({ days: true })}; - if (start.length === 0) { setErrors({ start: true })}; - if (end.length === 0) { setErrors({ end: true })}; - if (color.length === 0) { setErrors({ color: true })}; - if ( - errors.name ?? errors.abbreviation ?? errors.type ?? errors.days ?? errors.start - ?? errors.end ?? errors.color - ) { return; } - - setLoading(true); - - createProgram({ name, abbreviation, type, days, start, end, color }).catch( - err => handleAPIError(err) - ).then( - (result) => { - if (result.success) { - setName(""); - setAbbreviation(""); - setType(""); - setDays([]); - setStart(""); - setEnd(""); - setColor(""); - - onSubmit && onSubmit(result.data); - } else { - alert(result.error); - } - - setLoading(false); - } - ); - - }; - - const formTitle = (mode === "create" ? "Add New Program" : program?.name); - - return ( -
- {formTitle} -
- -
-
- ); -} \ No newline at end of file diff --git a/frontend/src/components/ProgramForm/Program.tsx b/frontend/src/components/ProgramForm/Program.tsx new file mode 100644 index 00000000..077e06f7 --- /dev/null +++ b/frontend/src/components/ProgramForm/Program.tsx @@ -0,0 +1,47 @@ +import { UseFormRegister, UseFormSetValue } from "react-hook-form"; + +import { cn } from "../../lib/utils"; +import { Textfield } from "../Textfield"; + +import { ProgramData } from "./types"; + + +type ProgramProperties = { + register: UseFormRegister; + classname?: string; + setCalendarValue: UseFormSetValue; + data: ProgramData | null; +} + + +export default function Program({ register, classname, setCalendarValue, data }: ProgramProperties) { + return ( +
+
+

Program Name

+ +
+
+

Program Abbreviation

+ +
+
+

Program Type

+
+
+

Program Days

+
+
+

Start Date

+ +
+
+

End Date

+ +
+
+

Color

+
+
+ ) +} \ No newline at end of file diff --git a/frontend/src/components/ProgramForm/types.ts b/frontend/src/components/ProgramForm/types.ts new file mode 100644 index 00000000..4df9ef28 --- /dev/null +++ b/frontend/src/components/ProgramForm/types.ts @@ -0,0 +1,17 @@ +/** + * + */ + + +/** + * + */ +export type ProgramData = { + name: string; + abbreviation: string; + type: string; + days: string[]; + start: string; + end: string; + color: string; +} diff --git a/frontend/src/components/ProgramFormButton.tsx b/frontend/src/components/ProgramFormButton.tsx new file mode 100644 index 00000000..794e491a --- /dev/null +++ b/frontend/src/components/ProgramFormButton.tsx @@ -0,0 +1,79 @@ +import { useState } from "react"; +import { SubmitHandler, useForm } from "react-hook-form"; + +import { cn } from "../lib/utils"; + +import { Button } from "./Button"; +import Program from "./ProgramForm/Program"; +import { ProgramData } from "./ProgramForm/types"; +import { Dialog, DialogClose, DialogContent, DialogTrigger } from "./ui/dialog"; + + +type BaseProperties = { + classname?: string; +} + + +type EditProperties = BaseProperties & { + type: "edit"; + data: ProgramData | null; +} + + +type AddProperties = BaseProperties & { + type: "add"; + data?: ProgramData | null; +} + + +type ProgramFormProperties = EditProperties | AddProperties; + + +export default function ProgramFormButton({type = "edit", data = null, classname}: ProgramFormProperties) { + const { register, setValue: setCalendarValue, reset, handleSubmit } = useForm(); + + const onSubmit: SubmitHandler = (formData: ProgramData) => { + reset(); + console.log(`${type} program`, formData); + } + + const [openForm, setOpenForm] = useState(false); + + return ( + <> + + + + + + + ); +} diff --git a/frontend/src/components/ProgramForm/types.ts b/frontend/src/components/ProgramForm/types.ts index 4df9ef28..e5986c89 100644 --- a/frontend/src/components/ProgramForm/types.ts +++ b/frontend/src/components/ProgramForm/types.ts @@ -1,17 +1,21 @@ /** - * + * */ - /** - * + * */ export type ProgramData = { - name: string; - abbreviation: string; - type: string; - days: string[]; - start: string; - end: string; - color: string; -} + name: string; + abbreviation: string; + type: string; + days: string[]; + start: string; + end: string; + color: string; + renewal: string; + hourly: string; + sessions: string[][]; +}; + +export type programColor = "teal" | "green" | "red" | "yellow" | "blue" | "violet" | "fuchsia"; diff --git a/frontend/src/components/ProgramFormButton.tsx b/frontend/src/components/ProgramFormButton.tsx index 6537120f..b1141318 100644 --- a/frontend/src/components/ProgramFormButton.tsx +++ b/frontend/src/components/ProgramFormButton.tsx @@ -5,10 +5,9 @@ import { useWindowSize } from "../hooks/useWindowSize"; import { cn } from "../lib/utils"; import { Button } from "./Button"; -import ProgramInfo from "./ProgramForm/ProgramInfo"; +import { ProgramInfo } from "./ProgramForm/ProgramInfo"; import { ProgramData } from "./ProgramForm/types"; -import { Dialog, DialogClose, DialogContent, DialogTrigger } from "./ui/dialog"; -import { ColorRadio } from "./Radio"; +import { Dialog, DialogClose, DialogContent, DialogContentSlide, DialogTrigger } from "./ui/dialog"; type BaseProperties = { classname?: string; @@ -53,7 +52,7 @@ export default function ProgramFormButton({ }} /> - +

{type === "add" ? "Add new program" : data?.name} @@ -65,18 +64,10 @@ export default function ProgramFormButton({ setCalendarValue={setCalendarValue} /> -
-
-
- Color (Cover) -
- - -
+
+ +
- + ) : ( @@ -105,9 +93,12 @@ export default function ProgramFormButton({ }} /> - + + +
Cancel
+
-

+

{type === "add" ? "Add new program" : data?.name}

-
-
- Color (Cover) -
- -
- -
diff --git a/frontend/src/components/Radio.tsx b/frontend/src/components/Radio.tsx index 82dcd481..24ef6f96 100644 --- a/frontend/src/components/Radio.tsx +++ b/frontend/src/components/Radio.tsx @@ -2,15 +2,16 @@ import { FieldValues, Path, UseFormRegister } from "react-hook-form"; import { cn } from "../lib/utils"; +import { programColor } from "./ProgramForm/types"; + type RadioProps = { options: string[]; className?: string; name: Path; register: UseFormRegister; + defaultValue?: programColor | undefined; }; -type programColor = "teal" | "green" | "red" | "yellow"; - type ColorRadioProps = RadioProps & { options: programColor[]; }; @@ -20,6 +21,9 @@ const programColors = { green: "bg-secondary_green", red: "bg-secondary_red", yellow: "bg-secondary_yellow", + blue: "bg-secondary_blue", + violet: "bg-secondary_violet", + fuchsia: "bg-secondary_fuchsia", }; export function Radio({ @@ -57,24 +61,26 @@ export function ColorRadio({ register, name, className, + defaultValue, }: ColorRadioProps) { return ( -
+
{options.map((option, index) => { return (
({
); } - diff --git a/frontend/src/components/Textfield.tsx b/frontend/src/components/Textfield.tsx index edee4ff3..c94e9a9c 100644 --- a/frontend/src/components/Textfield.tsx +++ b/frontend/src/components/Textfield.tsx @@ -139,6 +139,7 @@ export function Textfield({ type={type} onChange={handleInputChange} placeholder={placeholder} + defaultValue={defaultValue} />
); diff --git a/frontend/src/components/ui/dialog.tsx b/frontend/src/components/ui/dialog.tsx index f1186c5a..e882e05c 100644 --- a/frontend/src/components/ui/dialog.tsx +++ b/frontend/src/components/ui/dialog.tsx @@ -50,6 +50,27 @@ const DialogContent = React.forwardRef< )); DialogContent.displayName = DialogPrimitive.Content.displayName; +const DialogContentSlide = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + +)); +DialogContentSlide.displayName = DialogPrimitive.Content.displayName; + const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => (
); @@ -94,6 +115,7 @@ export { DialogClose, DialogTrigger, DialogContent, + DialogContentSlide, DialogHeader, DialogFooter, DialogTitle, diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index 034bdbed..23ef20e6 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -1,20 +1,20 @@ import { AppProps } from "next/app"; -import Landing from "../components/Landing"; +//import Landing from "../components/Landing"; import "../styles/global.css"; import "../styles/globals.css"; -// import Navigation from "../components/Navigation"; +import Navigation from "../components/Navigation"; function App({ Component, pageProps }: AppProps) { return ( - + // + // + // + - - // - // - // + ); } export default App; diff --git a/frontend/src/pages/programs.tsx b/frontend/src/pages/programs.tsx index 9e3a51f2..107389df 100644 --- a/frontend/src/pages/programs.tsx +++ b/frontend/src/pages/programs.tsx @@ -1,14 +1,16 @@ +import { ProgramData } from "../components/ProgramForm/types"; import ProgramFormButton from "../components/ProgramFormButton"; -import StudentFormButton from "../components/StudentFormButton"; +import sampleProgramData from "../sampleProgramData.json"; export default function Programs() { + console.log(sampleProgramData); return (
{" "}
- + {" "}
); diff --git a/frontend/src/sampleProgramData.json b/frontend/src/sampleProgramData.json index eb3cd32b..0b90e696 100644 --- a/frontend/src/sampleProgramData.json +++ b/frontend/src/sampleProgramData.json @@ -1,9 +1,14 @@ { - "name": "Green Bay Packers", - "abbreviation": "GBP", - "type": "regular", - "days": ["M", "TH"], - "start": "2024-01-01T00:00:00Z", - "end": "2024-01-14T00:00:00Z", - "color": "#4FA197" -} \ No newline at end of file + "name": "Green Bay Packers", + "abbreviation": "GBP", + "type": "varying", + "days": ["M", "Th"], + "start": "2024-01-01T00:00:00Z", + "end": "2024-01-14T00:00:00Z", + "color": "teal", + "sessions": [ + ["12:00", "2:00"], + ["3:00", "4:00"], + ["5:00", "6:00"] + ] +} diff --git a/frontend/src/styles/Button.module.css b/frontend/src/styles/Button.module.css index 400b9dc5..4fd19c55 100644 --- a/frontend/src/styles/Button.module.css +++ b/frontend/src/styles/Button.module.css @@ -14,6 +14,13 @@ padding-right: 1.5rem; } +.wide { + height: 3rem; + padding-left: 1.5rem; + padding-right: 1.5rem; + width: 100%; +} + .big { height: 10rem; padding-left: 1.5rem; diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index 9af4aca2..0cf0e9f7 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -164,6 +164,15 @@ module.exports = { secondary_green: { DEFAULT: "#B6BF0E", }, + secondary_blue: { + DEFAULT: "#5DADE2", + }, + secondary_violet: { + DEFAULT: "#7986CB", + }, + secondary_fuchsia: { + DEFAULT: "#EE6CEE", + }, }, borderRadius: { lg: "var(--radius)", @@ -184,6 +193,9 @@ module.exports = { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", }, + transitionProperty: { + height: "height", + }, }, }, plugins: [require("tailwindcss-animate")], From ed89c87851b729d88cba489fc3f693f9cc3fbd41 Mon Sep 17 00:00:00 2001 From: mraysu Date: Mon, 8 Apr 2024 19:25:28 -0700 Subject: [PATCH 18/23] Fixed Radio Issue --- frontend/src/components/ProgramForm/ProgramInfo.tsx | 8 +++++--- frontend/src/components/Radio.tsx | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/ProgramForm/ProgramInfo.tsx b/frontend/src/components/ProgramForm/ProgramInfo.tsx index bf63f0f6..d5f8875c 100644 --- a/frontend/src/components/ProgramForm/ProgramInfo.tsx +++ b/frontend/src/components/ProgramForm/ProgramInfo.tsx @@ -144,8 +144,9 @@ export function ProgramInfo({ id={"regular"} type="radio" value={"regular"} - checked={data?.type === "regular"} + defaultChecked={data?.type === "regular"} /> +
Regular @@ -155,12 +156,13 @@ export function ProgramInfo({
+
Varying diff --git a/frontend/src/components/Radio.tsx b/frontend/src/components/Radio.tsx index 24ef6f96..ec06566f 100644 --- a/frontend/src/components/Radio.tsx +++ b/frontend/src/components/Radio.tsx @@ -77,8 +77,9 @@ export function ColorRadio({ id={option + index} type="radio" value={option} - checked={defaultValue === option} + defaultChecked={option === defaultValue} /> + Date: Tue, 9 Apr 2024 02:00:18 -0700 Subject: [PATCH 19/23] Desktop Browser Archive --- frontend/public/Union.svg | 4 + .../components/ProgramForm/ProgramArchive.tsx | 34 ++++ frontend/src/components/ProgramFormButton.tsx | 150 ++++++++++++++++-- frontend/tailwind.config.ts | 2 +- 4 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 frontend/public/Union.svg create mode 100644 frontend/src/components/ProgramForm/ProgramArchive.tsx diff --git a/frontend/public/Union.svg b/frontend/public/Union.svg new file mode 100644 index 00000000..16b56009 --- /dev/null +++ b/frontend/public/Union.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/components/ProgramForm/ProgramArchive.tsx b/frontend/src/components/ProgramForm/ProgramArchive.tsx new file mode 100644 index 00000000..597c56bb --- /dev/null +++ b/frontend/src/components/ProgramForm/ProgramArchive.tsx @@ -0,0 +1,34 @@ +type props = { + label: string; +}; + +export default function ProgramArchiveHeader({ label }: props) { + return ( +
+

+ {label} +

+ + + + + +

Archive this program?

+
    +
  • It will be move to the ‘Archived’ programs tab
  • +
  • All ‘Joined’ students become ‘Archived’
  • +
  • You’ll be able to restore this program
  • +
+
+ ); +} diff --git a/frontend/src/components/ProgramFormButton.tsx b/frontend/src/components/ProgramFormButton.tsx index b1141318..aed005f1 100644 --- a/frontend/src/components/ProgramFormButton.tsx +++ b/frontend/src/components/ProgramFormButton.tsx @@ -5,8 +5,10 @@ import { useWindowSize } from "../hooks/useWindowSize"; import { cn } from "../lib/utils"; import { Button } from "./Button"; +import ProgramArchiveHeader from "./ProgramForm/ProgramArchive"; import { ProgramInfo } from "./ProgramForm/ProgramInfo"; import { ProgramData } from "./ProgramForm/types"; +import { Textfield } from "./Textfield"; import { Dialog, DialogClose, DialogContent, DialogContentSlide, DialogTrigger } from "./ui/dialog"; type BaseProperties = { @@ -31,16 +33,24 @@ export default function ProgramFormButton({ classname, }: ProgramFormProperties) { const { register, setValue: setCalendarValue, reset, handleSubmit } = useForm(); - const { width } = useWindowSize(); + const { + register: archiveRegister, + reset: archiveReset, + setValue: setArchiveCalendarValue, + getValues: getArchiveValue, + } = useForm<{ date: string }>(); + + const [openForm, setOpenForm] = useState(false); + const [openArchive, setOpenArchive] = useState(false); + const { width } = useWindowSize().windowSize; const isMobile = useMemo(() => width <= 640, [width]); const onSubmit: SubmitHandler = (formData: ProgramData) => { + setOpenForm(false); reset(); console.log(`${type} program`, formData); }; - const [openForm, setOpenForm] = useState(false); - return !isMobile ? ( <> @@ -54,6 +64,71 @@ export default function ProgramFormButton({
+ {type === "edit" && ( + +
+ +
+ +
+
+ +

Confirm by entering today's date

+ + +
+ +
{" "} + +
{" "} +
+
+
+ )} +

{type === "add" ? "Add new program" : data?.name}

@@ -65,18 +140,57 @@ export default function ProgramFormButton({ />
- -
+
+ +
@@ -109,7 +223,11 @@ export default function ProgramFormButton({ />
-
diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index 45503eb8..4ddf4267 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -136,7 +136,7 @@ module.exports = { foreground: "hsl(var(--secondary-foreground))", }, destructive: { - DEFAULT: "hsl(var(--destructive))", + DEFAULT: "#B93B45", foreground: "hsl(var(--destructive-foreground))", }, muted: { From 739b1c1a91e4562a3525f0fc4fbc8291444ab5fb Mon Sep 17 00:00:00 2001 From: mraysu Date: Tue, 9 Apr 2024 03:17:59 -0700 Subject: [PATCH 20/23] Archive Mobile --- .../components/ProgramForm/ProgramArchive.tsx | 4 +- .../components/ProgramForm/ProgramCancel.tsx | 72 +++++++++ frontend/src/components/ProgramFormButton.tsx | 146 +++++++++++------- 3 files changed, 162 insertions(+), 60 deletions(-) create mode 100644 frontend/src/components/ProgramForm/ProgramCancel.tsx diff --git a/frontend/src/components/ProgramForm/ProgramArchive.tsx b/frontend/src/components/ProgramForm/ProgramArchive.tsx index 597c56bb..a81b9b5f 100644 --- a/frontend/src/components/ProgramForm/ProgramArchive.tsx +++ b/frontend/src/components/ProgramForm/ProgramArchive.tsx @@ -23,8 +23,8 @@ export default function ProgramArchiveHeader({ label }: props) { /> -

Archive this program?

-
    +

    Archive this program?

    +
    • It will be move to the ‘Archived’ programs tab
    • All ‘Joined’ students become ‘Archived’
    • You’ll be able to restore this program
    • diff --git a/frontend/src/components/ProgramForm/ProgramCancel.tsx b/frontend/src/components/ProgramForm/ProgramCancel.tsx new file mode 100644 index 00000000..bec6e2d6 --- /dev/null +++ b/frontend/src/components/ProgramForm/ProgramCancel.tsx @@ -0,0 +1,72 @@ +import { FormEventHandler, useState } from "react"; + +import { Button } from "../Button"; +import { Dialog, DialogClose, DialogContent, DialogTrigger } from "../ui/dialog"; + +type cancelProps = { + onSubmit: FormEventHandler; + isMobile?: boolean; +}; + +export default function ProgramCancel({ onSubmit, isMobile = false }: cancelProps) { + const [openForm, setOpenForm] = useState(false); + return ( + + + {!isMobile ? ( +
+
+ + + + ); +} diff --git a/frontend/src/components/ProgramFormButton.tsx b/frontend/src/components/ProgramFormButton.tsx index aed005f1..aaab647f 100644 --- a/frontend/src/components/ProgramFormButton.tsx +++ b/frontend/src/components/ProgramFormButton.tsx @@ -6,6 +6,7 @@ import { cn } from "../lib/utils"; import { Button } from "./Button"; import ProgramArchiveHeader from "./ProgramForm/ProgramArchive"; +import ProgramCancel from "./ProgramForm/ProgramCancel"; import { ProgramInfo } from "./ProgramForm/ProgramInfo"; import { ProgramData } from "./ProgramForm/types"; import { Textfield } from "./Textfield"; @@ -139,58 +140,13 @@ export default function ProgramFormButton({ setCalendarValue={setCalendarValue} /> -
- -
-
- - +
+
@@ -208,13 +164,87 @@ export default function ProgramFormButton({ /> - -
Cancel
-
+ { + setOpenForm(false); + }} + isMobile={isMobile} + />
-

- {type === "add" ? "Add new program" : data?.name} -

+ {type === "edit" && ( + +
+ +
{ + setOpenArchive(true); + }} + > + Archive +
+
+
+ +
+
+ +

Confirm by entering today's date

+ + +
+ +
{" "} + +
{" "} +
+
+
+ )} + {type === "add" ? ( +

+ Add new program +

+ ) : ( +

{data?.name}

+ )} + Date: Tue, 9 Apr 2024 03:43:21 -0700 Subject: [PATCH 21/23] Build/linting fix --- frontend/src/pages/_app.tsx | 2 -- frontend/src/pages/profile.tsx | 2 +- frontend/src/pages/programs.tsx | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index f55fba6a..e409ae38 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -1,7 +1,5 @@ import { AppProps } from "next/app"; - //import Landing from "../components/Landing"; - import "../styles/global.css"; import "../styles/globals.css"; import { ReactElement, ReactNode } from "react"; diff --git a/frontend/src/pages/profile.tsx b/frontend/src/pages/profile.tsx index 543b4bc7..f10ae9ef 100644 --- a/frontend/src/pages/profile.tsx +++ b/frontend/src/pages/profile.tsx @@ -1,7 +1,7 @@ import { FieldValues, SubmitHandler, useForm } from "react-hook-form"; import { Checkbox } from "../components/Checkbox"; -import Radio from "../components/Radio"; +import { Radio } from "../components/Radio"; import { Textfield } from "../components/Textfield"; import { useRedirectToLoginIfNotSignedIn } from "@/hooks/redirect"; diff --git a/frontend/src/pages/programs.tsx b/frontend/src/pages/programs.tsx index 8c5f998a..6201bb91 100644 --- a/frontend/src/pages/programs.tsx +++ b/frontend/src/pages/programs.tsx @@ -2,7 +2,7 @@ import { ProgramData } from "../components/ProgramForm/types"; import ProgramFormButton from "../components/ProgramFormButton"; import sampleProgramData from "../sampleProgramData.json"; -import { useRedirectTo404IfNotAdmin, useRedirectToLoginIfNotSignedIn } from "@/hooks/redirect"; +//import { useRedirectTo404IfNotAdmin, useRedirectToLoginIfNotSignedIn } from "@/hooks/redirect"; export default function Programs() { //useRedirectToLoginIfNotSignedIn(); From 191b1b5c6acef32949f6169089f61fb4507f2387 Mon Sep 17 00:00:00 2001 From: mraysu Date: Tue, 9 Apr 2024 15:37:00 -0700 Subject: [PATCH 22/23] Reset default route to login --- frontend/src/pages/_app.tsx | 6 +++--- frontend/src/pages/index.tsx | 14 +++++++++++--- frontend/src/pages/programs.tsx | 11 +++-------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index e409ae38..087e5670 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -1,15 +1,15 @@ import { AppProps } from "next/app"; -//import Landing from "../components/Landing"; import "../styles/global.css"; import "../styles/globals.css"; import { ReactElement, ReactNode } from "react"; -import Navigation from "../components/Navigation"; +import Navigation from "@/components/Navigation"; // eslint-disable-next-line import/order import { NextPage } from "next"; import { UserContextProvider } from "@/contexts/user"; +// import Navigation from "../components/Navigation"; export type NextPageWithLayout

= NextPage & { getLayout?: (page: ReactElement) => ReactNode; }; @@ -24,4 +24,4 @@ function App({ Component, pageProps }: AppPropsWithLayout) { return {getLayout()}; } -export default App; +export default App; \ No newline at end of file diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index f144fce0..7f6044ba 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -1,5 +1,13 @@ -import Programs from "./programs"; +import { ReactElement } from "react"; -export default function Home() { - return ; +import Login from "./login"; + +import Landing from "@/components/Landing"; + +export default function MyApp() { + return ; } + +MyApp.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/frontend/src/pages/programs.tsx b/frontend/src/pages/programs.tsx index 6201bb91..8bd21266 100644 --- a/frontend/src/pages/programs.tsx +++ b/frontend/src/pages/programs.tsx @@ -1,20 +1,15 @@ -import { ProgramData } from "../components/ProgramForm/types"; import ProgramFormButton from "../components/ProgramFormButton"; -import sampleProgramData from "../sampleProgramData.json"; -//import { useRedirectTo404IfNotAdmin, useRedirectToLoginIfNotSignedIn } from "@/hooks/redirect"; +import { useRedirectTo404IfNotAdmin, useRedirectToLoginIfNotSignedIn } from "@/hooks/redirect"; export default function Programs() { - //useRedirectToLoginIfNotSignedIn(); - //useRedirectTo404IfNotAdmin(); + useRedirectToLoginIfNotSignedIn(); + useRedirectTo404IfNotAdmin(); return (

{" "}
-
- {" "} -
); } From 6e94c2cf8095fa473c7d26430b1f55c7043d8f1c Mon Sep 17 00:00:00 2001 From: mraysu Date: Tue, 9 Apr 2024 15:44:03 -0700 Subject: [PATCH 23/23] Linting Fix --- frontend/src/pages/_app.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index 087e5670..6050a34b 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -24,4 +24,4 @@ function App({ Component, pageProps }: AppPropsWithLayout) { return {getLayout()}; } -export default App; \ No newline at end of file +export default App;