From fc5e24d5555a7a82537a6ce3166389ca7c073b5f Mon Sep 17 00:00:00 2001 From: jnakaso Date: Sat, 8 Feb 2025 08:25:20 -0800 Subject: [PATCH 01/10] Trying a stepper --- src/api/cePlanService.ts | 12 +- src/api/types.ts | 1 + src/components/PlanDetails/GroupBoard.tsx | 45 ++-- src/components/PlanDetails/GroupCount.tsx | 63 ++++++ src/components/PlanDetails/Setup.tsx | 173 ---------------- src/components/PlanDetails/SetupStudents.tsx | 207 +++++++++++++++++++ src/components/PlanDetails/index.tsx | 120 ++++++++--- src/utils/props.ts | 11 + 8 files changed, 412 insertions(+), 220 deletions(-) create mode 100644 src/components/PlanDetails/GroupCount.tsx delete mode 100644 src/components/PlanDetails/Setup.tsx create mode 100644 src/components/PlanDetails/SetupStudents.tsx create mode 100644 src/utils/props.ts diff --git a/src/api/cePlanService.ts b/src/api/cePlanService.ts index dce8aef..50a1944 100644 --- a/src/api/cePlanService.ts +++ b/src/api/cePlanService.ts @@ -17,7 +17,7 @@ const TEST_PLAN = { id: '1', studentId: 's1', student: { - id: '', + id: 's1', name: 'Student 1', age: null, email: '', @@ -27,13 +27,14 @@ const TEST_PLAN = { availabilities: [] }, anchor: true, + priority: true, availabilities: [] }, { id: '2', studentId: 's2', student: { - id: '', + id: 's2', name: 'Student 2', age: null, email: '', @@ -43,13 +44,14 @@ const TEST_PLAN = { availabilities: [] }, anchor: false, + priority: true, availabilities: [] }, { id: '3', studentId: 's3', student: { - id: '', + id: 's3', name: 'Student 3', age: null, email: '', @@ -59,6 +61,7 @@ const TEST_PLAN = { availabilities: [] }, anchor: false, + priority: false, availabilities: [] } ], groups: [ @@ -82,7 +85,6 @@ const TEST_PLAN = { class CEPlanService { async getById(id: string): Promise { - console.log("get plan", id); return TEST_PLAN; } @@ -93,7 +95,7 @@ class CEPlanService { async duplicate(plan: Plan): Promise { alert(` '${plan.name}' would be duplicated ${plan.name}`) - return [{...TEST_PLAN}] + return [{ ...TEST_PLAN }] } } diff --git a/src/api/types.ts b/src/api/types.ts index 5810f01..b4ee0c9 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -32,6 +32,7 @@ type Enrollment = { studentId: string; student: Student; anchor: boolean; + priority: boolean; availabilities: Availability[]; } diff --git a/src/components/PlanDetails/GroupBoard.tsx b/src/components/PlanDetails/GroupBoard.tsx index a318b57..c2305fb 100644 --- a/src/components/PlanDetails/GroupBoard.tsx +++ b/src/components/PlanDetails/GroupBoard.tsx @@ -8,23 +8,35 @@ import { ReactNode, useEffect, useState } from "react"; -import { StarFilled } from "@ant-design/icons"; +import { ExclamationCircleFilled, StarFilled } from "@ant-design/icons"; +import { DDCategory, DDType, DragAndDrop } from '@digitalaidseattle/draganddrop'; import { Box, Button, Card, CardContent, + Stack, Typography } from "@mui/material"; -import { DragAndDrop, DDCategory, DDType } from '@digitalaidseattle/draganddrop'; - +import { PlanProps } from "../../utils/props"; export const StudentCard = (props: { enrollment: Enrollment }) => { - + const anchor = props.enrollment.anchor ? 'green' : 'gray;' + const priority = props.enrollment.priority ? 'green' : 'gray;' return ( - {props.enrollment.anchor && } {props.enrollment.student.name} + + + {props.enrollment.anchor && + + } + {props.enrollment.priority && + + } + + {props.enrollment.student.name} + ); @@ -32,24 +44,22 @@ export const StudentCard = (props: { enrollment: Enrollment }) => { type EnrollmentWrapper = Enrollment & DDType -export const GroupBoard = (props: { plan: Plan | undefined }) => { +export const GroupBoard: React.FC = ({ plan }) => { const [categories, setCategories] = useState[]>(); useEffect(() => { - if (props.plan) { - setCategories(props.plan.groups.map(group => { - return { label: group.groupNo, value: group.groupNo } - })) - } - }, [props]) + setCategories(plan.groups.map(group => { + return { label: group.groupNo, value: group.groupNo } + })) + }, [plan]) function handleChange(c: Map, t: Enrollment) { console.log(c, t) } function isCategory(item: EnrollmentWrapper, category: DDCategory): boolean { - if (props.plan) { - const group = props.plan.groups.find(group => group.groupNo === category.value); + if (plan) { + const group = plan.groups.find(group => group.groupNo === category.value); return group ? group.studentIds.includes(item.studentId) : false; } return false; @@ -61,17 +71,16 @@ export const GroupBoard = (props: { plan: Plan | undefined }) => { return ( <> - - <>{props.plan && categories && + <>{plan && categories && , e: Enrollment) => handleChange(c, e)} - items={props.plan.enrollments} + items={plan.enrollments} categories={categories} isCategory={isCategory} cardRenderer={cellRender} />} - {!props.plan && + {!plan && No plan found. } diff --git a/src/components/PlanDetails/GroupCount.tsx b/src/components/PlanDetails/GroupCount.tsx new file mode 100644 index 0000000..00ea2e1 --- /dev/null +++ b/src/components/PlanDetails/GroupCount.tsx @@ -0,0 +1,63 @@ + +/** + * GroupCount.tsx + * + * @copyright 2024 Digital Aid Seattle + * + */ + +import { useEffect, useState } from "react"; + +import { + Box, + Card, + CardContent, + Slider, + Stack, + Typography +} from "@mui/material"; +import { PlanProps } from "../../utils/props"; + +export const GroupCount: React.FC = ({ plan }) => { + const [values, setValues] = useState([0, 10]); + + const [min, setMin] = useState(5); + const [max, setMax] = useState(10); + + useEffect(() => { + // TODO base values, min, & max on student counts + setValues([5, 10]) + setMin(5); + setMax(10); + }, [plan]) + + function handleChange(_event: Event, newValue: number | number[]): void { + console.log(newValue) + setValues(newValue as number[]); + } + + return ( + <> + + + + + Number of Groups + + + + + + + + + ) +}; diff --git a/src/components/PlanDetails/Setup.tsx b/src/components/PlanDetails/Setup.tsx deleted file mode 100644 index 67d4218..0000000 --- a/src/components/PlanDetails/Setup.tsx +++ /dev/null @@ -1,173 +0,0 @@ -/** - * SetupPanel.tsx - * - * Example of integrating tickets with data-grid - */ -import { useContext, useEffect, useState } from 'react'; - -// material-ui -import { - Box, - Button, - Stack, - Switch -} from '@mui/material'; -import { - DataGrid, - GridColDef, - GridRenderCellParams, - GridRowSelectionModel, - GridSortModel, - useGridApiRef -} from '@mui/x-data-grid'; - -// third-party - -// project import -import { StarFilled } from '@ant-design/icons'; -import { LoadingContext, RefreshContext } from '@digitalaidseattle/core'; -import { PageInfo, QueryModel } from '@digitalaidseattle/supabase'; -import { studentService } from '../../api/ceStudentService'; - -const PAGE_SIZE = 10; - -const getColumns = (): GridColDef[] => { - return [ - { - field: 'anchor', - headerName: 'Anchor', - width: 100, - type: 'boolean', - renderCell: (_param: GridRenderCellParams) => { - return - } - }, { - field: 'priority', - headerName: 'Priority', - width: 100, - type: 'boolean', - renderCell: (_param: GridRenderCellParams) => { - return - } - }, - { - field: 'name', - headerName: 'Name', - width: 150, - }, - { - field: 'email', - headerName: 'Email', - width: 140, - }, - { - field: 'city', - headerName: 'City', - width: 140, - }, - { - field: 'country', - headerName: 'Country', - width: 140, - }, - { - field: 'availability', - headerName: 'Availability', - width: 140, - } - ]; -} - -export default function Setup() { - const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: PAGE_SIZE }); - const [sortModel, setSortModel] = useState([{ field: 'created_at', sort: 'desc' }]) - const [rowSelectionModel, setRowSelectionModel] = useState(); - const [pageInfo, setPageInfo] = useState>({ rows: [], totalRowCount: 0 }); - const apiRef = useGridApiRef(); - const { setLoading } = useContext(LoadingContext); - const { refresh } = useContext(RefreshContext); - - useEffect(() => { - if (paginationModel && sortModel) { - const queryModel = { - page: paginationModel.page, - pageSize: paginationModel.pageSize, - sortField: sortModel.length === 0 ? 'created_at' : sortModel[0].field, - sortDirection: sortModel.length === 0 ? 'created_at' : sortModel[0].sort - } as QueryModel - studentService.find(queryModel) - .then((sess) => setPageInfo(sess)) - } - }, [paginationModel, sortModel]) - - useEffect(() => { - const queryModel = { - page: paginationModel.page, - pageSize: paginationModel.pageSize, - sortField: sortModel.length === 0 ? 'created_at' : sortModel[0].field, - sortDirection: sortModel.length === 0 ? 'created_at' : sortModel[0].sort - } as QueryModel - setLoading(true); - studentService.find(queryModel) - .then((pi) => setPageInfo(pi)) - .finally(() => setLoading(false)) - - }, [refresh]) - - const applyAction = () => { - alert(`Apply some action to ${rowSelectionModel ? rowSelectionModel.length : 0} items.`) - } - - const addStudent = () => { - alert(`Add student not implemented`) - } - - return ( - - - - - - - - - ); -} diff --git a/src/components/PlanDetails/SetupStudents.tsx b/src/components/PlanDetails/SetupStudents.tsx new file mode 100644 index 0000000..e586564 --- /dev/null +++ b/src/components/PlanDetails/SetupStudents.tsx @@ -0,0 +1,207 @@ +/** + * SetupPanel.tsx + * + * Example of integrating tickets with data-grid + */ +import { useEffect, useState } from 'react'; + +// material-ui +import { + Box, + Button, + Stack +} from '@mui/material'; +import { + DataGrid, + GridColDef, + GridRenderCellParams, + GridRowId, + GridRowSelectionModel, + GridSortModel, + useGridApiRef +} from '@mui/x-data-grid'; + +// third-party + +// project import +import { ExclamationCircleFilled, StarFilled } from '@ant-design/icons'; +import { PageInfo } from '@digitalaidseattle/supabase'; +import { PlanProps } from '../../utils/props'; + +const PAGE_SIZE = 10; + +type EnrolledStudent = Student & { enrollmentId: string, anchor: boolean, priority: boolean } + +export const SetupStudents: React.FC = ({ plan }) => { + + const apiRef = useGridApiRef(); + + const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: PAGE_SIZE }); + const [sortModel, setSortModel] = useState([{ field: 'created_at', sort: 'desc' }]) + const [rowSelectionModel, setRowSelectionModel] = useState(); + const [pageInfo, setPageInfo] = useState>({ rows: [], totalRowCount: 0 }); + + useEffect(() => { + const enrolledStudents = plan.enrollments.map(en => { + return { + ...en.student, + enrollmentId: en.id, + anchor: en.anchor, + priority: en.priority, + } + }); + setPageInfo({ + rows: enrolledStudents, + totalRowCount: enrolledStudents.length + }) + }, [plan]) + + const applyAnchor = () => { + rowSelectionModel?.forEach((n: GridRowId) => { + const row = pageInfo.rows.find(r => r.id === n) + if (row) { + row.anchor = true; + } + }) + setPageInfo({ ...pageInfo }); + } + + const applyPriority = () => { + rowSelectionModel?.forEach((n: GridRowId) => { + const row = pageInfo.rows.find(r => r.id === n) + if (row) { + row.priority = true; + } + }) + setPageInfo({ ...pageInfo }); + } + + const addStudent = () => { + alert(`Add student not implemented yet`) + } + + const removeStudent = () => { + alert(`Remove student not implemented yet`) + } + + const toggleAnchor = (student: EnrolledStudent) => { + student.anchor = !student.anchor; + setPageInfo({ ...pageInfo }); + } + + const togglePriority = (student: EnrolledStudent) => { + student.priority = !student.priority + setPageInfo({ ...pageInfo }); + } + + const getColumns = (): GridColDef[] => { + return [ + { + field: 'anchor', + headerName: 'Anchor', + width: 100, + type: 'boolean', + renderCell: (param: GridRenderCellParams) => { + return toggleAnchor(param.row)} /> + } + }, + { + field: 'priority', + headerName: 'Priority', + width: 100, + type: 'boolean', + renderCell: (param: GridRenderCellParams) => { + return togglePriority(param.row)} /> + } + }, + { + field: 'name', + headerName: 'Name', + width: 150, + }, + { + field: 'email', + headerName: 'Email', + width: 140, + }, + { + field: 'city', + headerName: 'City', + width: 140, + }, + { + field: 'country', + headerName: 'Country', + width: 140, + }, + { + field: 'availability', + headerName: 'Availability', + width: 140, + } + ]; + } + + return ( + + + + + + + + {plan && + + } + + ); +} diff --git a/src/components/PlanDetails/index.tsx b/src/components/PlanDetails/index.tsx index 6b1eb8c..2c08063 100644 --- a/src/components/PlanDetails/index.tsx +++ b/src/components/PlanDetails/index.tsx @@ -6,40 +6,112 @@ */ import { MainCard } from '@digitalaidseattle/mui'; -import { Box, Tab, Tabs } from '@mui/material'; +import { Box, Button, Stack, Step, StepLabel, Stepper } from '@mui/material'; import { useState } from "react"; -import { TabPanel } from "../TabPanel"; +import { PlanProps } from '../../utils/props'; import { TextEdit } from "../TextEdit"; import { GroupBoard } from "./GroupBoard"; -import Setup from "./Setup"; +import { GroupCount } from './GroupCount'; +import { SetupStudents } from './SetupStudents'; -export const PlanDetails = (props: { plan: Plan }) => { - const [value, setValue] = useState(0); - const plan = props.plan; +// const TabbedDetails: React.FC = ({ plan }) => { +// const [value, setValue] = useState(0); - const handleChange = (_event: React.SyntheticEvent, newValue: number) => { - setValue(newValue); +// const changeTab = (_event: React.SyntheticEvent, newValue: number) => { +// setValue(newValue); +// }; + +// return ( +// <> +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// ); +// } + +const SteppedDetails: React.FC = ({ plan }) => { + const steps = ['Setup Students', 'Set Group Number', 'Review']; + + const [activeStep, setActiveStep] = useState(0); + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); }; + const handleNext = () => { + setActiveStep((prevActiveStep) => prevActiveStep + 1); + }; + + return ( + <> + + + {steps.map((label) => { + const stepProps: { completed?: boolean } = {}; + const labelProps: { + optional?: React.ReactNode; + } = {}; + + return ( + + {label} + + ); + })} + + <> + + + + + + {activeStep === 0 && } + {activeStep === 1 && } + {activeStep === 2 && } + + + ); +} + + +export const PlanDetails: React.FC = ({ plan }) => { + // TODO add breadcrumbs return ( - - alert(`TODO save : ${text} name`)} /> - alert(`TODO note save : ${text}`)} /> - - - - - - - - - - - - - + + + alert(`TODO save : ${text} name`)} /> + alert(`TODO note save : ${text}`)} /> + + + {/* */} + + ); } diff --git a/src/utils/props.ts b/src/utils/props.ts new file mode 100644 index 0000000..2afa1ce --- /dev/null +++ b/src/utils/props.ts @@ -0,0 +1,11 @@ +/** + * App.tsx + * + * @copyright 2025 Digital Aid Seattle + * + */ + +export interface PlanProps { + plan: Plan; + onChange?: (plan: Plan) => void; +} \ No newline at end of file From d81cbb765d8b3035aac7e6f3997c73aad31101d2 Mon Sep 17 00:00:00 2001 From: jnakaso Date: Sat, 8 Feb 2025 08:28:39 -0800 Subject: [PATCH 02/10] lint --- src/api/cePlanService.ts | 2 +- src/components/PlanDetails/GroupBoard.tsx | 1 - src/components/PlanDetails/index.tsx | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/api/cePlanService.ts b/src/api/cePlanService.ts index 50a1944..01fddc8 100644 --- a/src/api/cePlanService.ts +++ b/src/api/cePlanService.ts @@ -84,7 +84,7 @@ const TEST_PLAN = { } class CEPlanService { - async getById(id: string): Promise { + async getById(_id: string): Promise { return TEST_PLAN; } diff --git a/src/components/PlanDetails/GroupBoard.tsx b/src/components/PlanDetails/GroupBoard.tsx index c2305fb..c0e1e00 100644 --- a/src/components/PlanDetails/GroupBoard.tsx +++ b/src/components/PlanDetails/GroupBoard.tsx @@ -12,7 +12,6 @@ import { ExclamationCircleFilled, StarFilled } from "@ant-design/icons"; import { DDCategory, DDType, DragAndDrop } from '@digitalaidseattle/draganddrop'; import { Box, - Button, Card, CardContent, Stack, diff --git a/src/components/PlanDetails/index.tsx b/src/components/PlanDetails/index.tsx index 2c08063..c280903 100644 --- a/src/components/PlanDetails/index.tsx +++ b/src/components/PlanDetails/index.tsx @@ -43,7 +43,7 @@ import { SetupStudents } from './SetupStudents'; // } const SteppedDetails: React.FC = ({ plan }) => { - const steps = ['Setup Students', 'Set Group Number', 'Review']; + const steps = ['Setup Students', 'Number of Groups', 'Review']; const [activeStep, setActiveStep] = useState(0); From ddb9fbe0455a22f130260161132e455c3766a430 Mon Sep 17 00:00:00 2001 From: jnakaso Date: Sat, 8 Feb 2025 08:31:20 -0800 Subject: [PATCH 03/10] more lint --- src/api/ceCohortService.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api/ceCohortService.ts b/src/api/ceCohortService.ts index 381f696..00d0a60 100644 --- a/src/api/ceCohortService.ts +++ b/src/api/ceCohortService.ts @@ -29,6 +29,7 @@ const TEST_PLAN = { availabilities: [] }, anchor: true, + priority: false, availabilities: [] }, { @@ -45,6 +46,7 @@ const TEST_PLAN = { availabilities: [] }, anchor: false, + priority: false, availabilities: [] }, { @@ -61,6 +63,7 @@ const TEST_PLAN = { availabilities: [] }, anchor: false, + priority: false, availabilities: [] } ], From 5ce21e6268af09e73abe26bee2375c933ba10559 Mon Sep 17 00:00:00 2001 From: jnakaso Date: Sun, 16 Feb 2025 11:39:46 -0800 Subject: [PATCH 04/10] added plans --- src/api/ceEnrollmentService.ts | 3 +- src/api/cePlanService.ts | 129 ++++++------------ src/api/entityService.ts | 4 +- src/api/types.ts | 15 +- src/components/PlanCard.tsx | 22 ++- src/components/PlanDetails/GroupBoard.tsx | 2 +- src/components/PlanDetails/index.tsx | 2 +- src/pages/cohort/index.tsx | 46 +++++-- src/pages/plan/index.tsx | 2 +- .../migrations/000014_db_plan_updates.sql | 2 + 10 files changed, 106 insertions(+), 121 deletions(-) create mode 100644 supabase/migrations/000014_db_plan_updates.sql diff --git a/src/api/ceEnrollmentService.ts b/src/api/ceEnrollmentService.ts index 9fe1cf1..ec93750 100644 --- a/src/api/ceEnrollmentService.ts +++ b/src/api/ceEnrollmentService.ts @@ -14,11 +14,12 @@ class CEEnrollmentService extends EntityService { async getStudents(cohort: Cohort): Promise { return await supabaseClient - .from('enrollment') + .from(this.tableName) .select('student(*)') .eq('cohort_id', cohort.id) .then(resp => resp.data as unknown as Student[]); } + } const enrollmentService = new CEEnrollmentService('enrollment') diff --git a/src/api/cePlanService.ts b/src/api/cePlanService.ts index 1e02049..facc3af 100644 --- a/src/api/cePlanService.ts +++ b/src/api/cePlanService.ts @@ -5,104 +5,51 @@ * */ -import { Plan } from "./types"; - -const TEST_PLAN = { - id: '1', - name: 'Plan1', - numberOfGroups: 3, - rating: 0, - notes: '', - cohortId: "sess1", - enrollments: [ - { - id: '1', - cohortId: '1', - studentId: 's1', - student: { - id: '', - name: 'Student 1', - age: null, - email: '', - city: '', - state: '', - country: '', - availabilities: [] - }, - anchor: true, - availabilities: [] - }, - { - id: '2', - cohortId: '1', - studentId: 's2', - student: { - id: '', - name: 'Student 2', - age: null, - email: '', - city: '', - state: '', - country: '', - availabilities: [] - }, - anchor: false, - availabilities: [] - }, - { - id: '3', - cohortId: '1', - studentId: 's3', - student: { - id: '', - name: 'Student 3', - age: null, - email: '', - city: '', - state: '', - country: '', - availabilities: [] - }, - anchor: false, - availabilities: [] - } - ], groups: [ - { - id: undefined, - groupNo: 'Group 1', - studentIds: ['s1'] - }, - { - id: undefined, - groupNo: 'Group 2', - studentIds: ['s2', 's3'] - }, - { - id: undefined, - groupNo: 'Group 3', - studentIds: [] - } - ], -} - -class CEPlanService { - async getById(id: string): Promise { - console.log("get plan", id); - return TEST_PLAN; +import { supabaseClient } from "@digitalaidseattle/supabase"; +import { v4 as uuidv4 } from 'uuid'; +import { EntityService } from "./entityService"; +import { Cohort, Identifier, Plan } from "./types"; + +class CEPlanService extends EntityService { + async create(cohort: Cohort): Promise { + const proposed: Plan = { + id: uuidv4(), + name: 'New Plan', + note: '', + cohort_id: cohort.id + } as Plan + return this.insert(proposed) + .then(plan => { + // TODO add placement + return plan; + }) } - async findByCohortId(cohortId: string): Promise { - console.log("get plans for cohort ", cohortId); - return [TEST_PLAN] + async duplicate(plan: Plan): Promise { + const proposed: Plan = { + id: uuidv4(), + name: plan.name + ' (copy)', + note: '', + cohort_id: plan.cohort_id + } as Plan + return this.insert(proposed) + .then(plan => { + // TODO copy placements + // TODO copy groups? + return plan; + }) } - async duplicate(plan: Plan): Promise { - alert(` '${plan.name}' would be duplicated ${plan.name}`) - return [{...TEST_PLAN}] + async findByCohortId(cohort_id: Identifier): Promise { + return await supabaseClient + .from(this.tableName) + .select('*') + .eq('cohort_id', cohort_id) + .then(resp => resp.data as Plan[]); } } -const planService = new CEPlanService() +const planService = new CEPlanService('plan') export { planService }; diff --git a/src/api/entityService.ts b/src/api/entityService.ts index b3b7778..b567d8a 100644 --- a/src/api/entityService.ts +++ b/src/api/entityService.ts @@ -6,7 +6,7 @@ */ import { PageInfo, QueryModel, supabaseClient } from "@digitalaidseattle/supabase"; -import { Entity } from "./types"; +import { Entity, Identifier } from "./types"; abstract class EntityService { @@ -141,7 +141,7 @@ abstract class EntityService { } } - async delete(entityId: string): Promise { + async delete(entityId: Identifier): Promise { try { const { error } = await supabaseClient .from(this.tableName) diff --git a/src/api/types.ts b/src/api/types.ts index b6983ae..8fe3d1f 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -4,8 +4,10 @@ * @copyright 2025 Digital Aid Seattle * */ +type Identifier = string | number; + type Entity = { - id: string | number; + id: Identifier; } type Availability = { @@ -41,8 +43,8 @@ type Group = { } type Enrollment = Entity & { - cohort_id: string | number; - student_id: string | number; + cohort_id: Identifier; + student_id: Identifier; } type Placement = { @@ -56,12 +58,12 @@ type Placement = { type Plan = Entity & { name: string; - cohortId: string; + cohort_id: Identifier; numberOfGroups: number; - enrollments: Enrollment[] + placements: Placement[]; groups: Group[]; rating: number; - notes: string; + note: string; } export type { @@ -69,6 +71,7 @@ export type { Enrollment, Entity, FailedStudent, + Identifier, Student, Cohort, Group, diff --git a/src/components/PlanCard.tsx b/src/components/PlanCard.tsx index 1567ab1..a4dfff8 100644 --- a/src/components/PlanCard.tsx +++ b/src/components/PlanCard.tsx @@ -13,9 +13,12 @@ import React, { useState } from "react"; import { useNavigate } from "react-router"; import { planService } from "../api/cePlanService"; import { Plan } from "../api/types"; +import { useNotifications } from "@digitalaidseattle/core"; export const PlanCard = (props: { plan: Plan }) => { + const notifications = useNotifications(); + const [anchorEl, setAnchorEl] = useState(null); const showMenu = Boolean(anchorEl); @@ -41,17 +44,22 @@ export const PlanCard = (props: { plan: Plan }) => { setAnchorEl(null); }; - const handleDelete = () => { + const handleDelete = () => { setOpenDeleteDialog(true) setAnchorEl(null); }; - const doDelete = () => { - alert(' TODO delete plan') - setOpenDeleteDialog(false); - setAnchorEl(null); + const doDelete = () => { + if (props.plan) { + planService.delete(props.plan.id) + .then(() => { + notifications.success(`Deleted plan ${props.plan.name}.`); + setOpenDeleteDialog(false); + setAnchorEl(null); + }) + } }; - + return ( { {props.plan.name} - Notes : {props.plan.notes} + Notes : {props.plan.note} Stats : # of students, groups, etc. { <>{props.plan && categories && , e: Placement) => handleChange(c, e)} - items={props.plan.enrollments} + items={props.plan.placements} categories={categories} isCategory={isCategory} cardRenderer={cellRender} diff --git a/src/components/PlanDetails/index.tsx b/src/components/PlanDetails/index.tsx index 959c81c..8fba126 100644 --- a/src/components/PlanDetails/index.tsx +++ b/src/components/PlanDetails/index.tsx @@ -27,7 +27,7 @@ export const PlanDetails = (props: { plan: Plan }) => { alert(`TODO save : ${text} name`)} /> - alert(`TODO note save : ${text}`)} /> + alert(`TODO note save : ${text}`)} /> diff --git a/src/pages/cohort/index.tsx b/src/pages/cohort/index.tsx index ea84047..3a2f70f 100644 --- a/src/pages/cohort/index.tsx +++ b/src/pages/cohort/index.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { useParams } from 'react-router'; // material-ui @@ -10,21 +10,32 @@ import { MainCard } from '@digitalaidseattle/mui'; import { cohortService } from '../../api/ceCohortService'; import { PlanCard } from '../../components/PlanCard'; import { TextEdit } from '../../components/TextEdit'; -import { useNotifications } from '@digitalaidseattle/core'; -import { Cohort } from '../../api/types'; +import { RefreshContext, useNotifications } from '@digitalaidseattle/core'; +import { Cohort, Plan } from '../../api/types'; +import { planService } from '../../api/cePlanService'; const CohortPage: React.FC = () => { const { id: cohortId } = useParams(); + const { refresh, setRefresh } = useContext(RefreshContext); + const notifications = useNotifications(); const [cohort, setCohort] = useState(); + const [plans, setPlans] = useState([]); useEffect(() => { if (cohortId) { cohortService.getById(cohortId) .then(cohort => setCohort(cohort)) } - }, [cohortId]); + }, [refresh, cohortId]); + + useEffect(() => { + if (cohort) { + planService.findByCohortId(cohort.id) + .then(plans => setPlans(plans)) + } + }, [cohort]); function handleNameChange(newText: string) { if (cohort) { @@ -32,23 +43,36 @@ const CohortPage: React.FC = () => { .update(cohort.id.toString(), { name: newText }) // FIXME change ID to UUID .then(updated => { setCohort(updated); - notifications.success(`Cohort ${updated.name} updated.`) + notifications.success(`Cohort ${updated.name} updated.`); + setRefresh(refresh + 1); }); } } + function handleNewPlan() { + if (cohort) { + planService.create(cohort) + .then(plan => { + notifications.success(`Plan ${plan.name} created.`); + }) + } + } + return (cohort && handleNameChange(val)} /> - + {/* Consider an alternate : switch between selected plan and all plans */} - - {cohort.plans.map(plan => - - )} - + + + {plans.map(plan => + + )} + + + ) }; diff --git a/src/pages/plan/index.tsx b/src/pages/plan/index.tsx index 3b5d531..36c9eaa 100644 --- a/src/pages/plan/index.tsx +++ b/src/pages/plan/index.tsx @@ -16,7 +16,7 @@ const PlanPage: React.FC = () => { useEffect(() => { if (planId) { planService.getById(planId) - .then(p => setPlan(p)) + .then(p => setPlan(p!)) } }, [planId]) return (plan && diff --git a/supabase/migrations/000014_db_plan_updates.sql b/supabase/migrations/000014_db_plan_updates.sql new file mode 100644 index 0000000..8d96773 --- /dev/null +++ b/supabase/migrations/000014_db_plan_updates.sql @@ -0,0 +1,2 @@ +ALTER TABLE plan DROP COLUMN id CASCADE; +ALTER TABLE plan ADD COLUMN id UUID PRIMARY KEY; \ No newline at end of file From abfb9c3e84a03bb57038101b84b99e3105bb0be4 Mon Sep 17 00:00:00 2001 From: jnakaso Date: Mon, 17 Feb 2025 14:07:30 -0800 Subject: [PATCH 05/10] add plan --- src/api/ceCohortService.ts | 17 +++++---- src/api/ceEnrollmentService.ts | 7 +++- src/api/cePlacementService.ts | 26 +++++++++++++ src/api/cePlanService.ts | 30 ++++++++++++--- src/api/entityService.ts | 4 +- src/api/types.ts | 8 ++-- src/components/PlanCard.tsx | 37 +++++++++++++------ src/components/PlanDetails/Setup.tsx | 2 +- src/pages/cohort/index.tsx | 11 ++++-- .../000015_db_placement_updates.sql | 14 +++++++ 10 files changed, 118 insertions(+), 38 deletions(-) create mode 100644 src/api/cePlacementService.ts create mode 100644 supabase/migrations/000015_db_placement_updates.sql diff --git a/src/api/ceCohortService.ts b/src/api/ceCohortService.ts index 463fb20..a9b27ec 100644 --- a/src/api/ceCohortService.ts +++ b/src/api/ceCohortService.ts @@ -17,12 +17,13 @@ class CECohortService extends EntityService { async create(): Promise { return studentService.findUnenrolled() .then(students => { - return cohortService.insert( - { - id: uuidv4(), - name: `(New) Cohort`, - } as Cohort - ) + return cohortService + .insert( + { + id: uuidv4(), + name: `(New) Cohort`, + } as Cohort + ) .then(cohort => { const enrollments = students.map(student => { return { @@ -38,9 +39,9 @@ class CECohortService extends EntityService { async getById(entityId: string | number, select?: string): Promise { try { - const cohort = await super.getById(entityId, select ?? '*, plan(*)') + const cohort = await super.getById(entityId, select ?? '*, enrollment(*), plan(*)'); + console.log(cohort); if (cohort) { - console.log(cohort) return { ...cohort, plans: [] // TODO join into plans diff --git a/src/api/ceEnrollmentService.ts b/src/api/ceEnrollmentService.ts index ec93750..146acb2 100644 --- a/src/api/ceEnrollmentService.ts +++ b/src/api/ceEnrollmentService.ts @@ -17,9 +17,12 @@ class CEEnrollmentService extends EntityService { .from(this.tableName) .select('student(*)') .eq('cohort_id', cohort.id) - .then(resp => resp.data as unknown as Student[]); + .then(resp => { + const objects = resp.data as unknown as any[]; + return objects.map(ss => ss.student) as Student[]; + }); } - + } const enrollmentService = new CEEnrollmentService('enrollment') diff --git a/src/api/cePlacementService.ts b/src/api/cePlacementService.ts new file mode 100644 index 0000000..3dbe95e --- /dev/null +++ b/src/api/cePlacementService.ts @@ -0,0 +1,26 @@ +/** + * ceCohortService.ts + * + * @copyright 2025 Digital Aid Seattle + * + */ + +import { supabaseClient } from "@digitalaidseattle/supabase"; +import { EntityService } from "./entityService"; +import { Identifier, Placement } from "./types"; + + +class CEPlacementService extends EntityService { + + async findByPlanId(planId: Identifier): Promise { + return await supabaseClient + .from(this.tableName) + .select('*, student(*)') + .eq('plan_id', planId) + .then(resp => resp.data as Placement[]); + } +} + +const placementService = new CEPlacementService('placement') +export { placementService }; + diff --git a/src/api/cePlanService.ts b/src/api/cePlanService.ts index facc3af..3bd52e4 100644 --- a/src/api/cePlanService.ts +++ b/src/api/cePlanService.ts @@ -7,8 +7,10 @@ import { supabaseClient } from "@digitalaidseattle/supabase"; import { v4 as uuidv4 } from 'uuid'; +import { placementService } from "./cePlacementService"; import { EntityService } from "./entityService"; -import { Cohort, Identifier, Plan } from "./types"; +import { Cohort, Identifier, Placement, Plan } from "./types"; +import { enrollmentService } from "./ceEnrollmentService"; class CEPlanService extends EntityService { async create(cohort: Cohort): Promise { @@ -18,11 +20,27 @@ class CEPlanService extends EntityService { note: '', cohort_id: cohort.id } as Plan - return this.insert(proposed) - .then(plan => { - // TODO add placement - return plan; - }) + // + return enrollmentService.getStudents(cohort) + .then(students => { + return this.insert(proposed) + .then(plan => { + const placements = students.map(student => { + return { + plan_id: plan.id, + student_id: student.id, + anchor: false, + priority: 0 + } as Placement + }) + return placementService + .batchInsert(placements) + .then(createdPlacements => { + plan.placements = createdPlacements; + return plan; + }) + }) + }); } async duplicate(plan: Plan): Promise { diff --git a/src/api/entityService.ts b/src/api/entityService.ts index b567d8a..3faa063 100644 --- a/src/api/entityService.ts +++ b/src/api/entityService.ts @@ -88,7 +88,7 @@ abstract class EntityService { } } - async batchInsert(entities: T[], select?: string): Promise { + async batchInsert(entities: T[], select?: string): Promise { try { const { data, error } = await supabaseClient .from(this.tableName) @@ -98,7 +98,7 @@ abstract class EntityService { console.error('Error inserting entity:', error.message); throw new Error('Failed to insert entity'); } - return data as unknown as T; + return data as unknown as T[]; } catch (err) { console.error('Unexpected error during insertion:', err); throw err; diff --git a/src/api/types.ts b/src/api/types.ts index 8fe3d1f..3e399d5 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -47,12 +47,12 @@ type Enrollment = Entity & { student_id: Identifier; } -type Placement = { - id: string; - cohortId: string; - studentId: string; +type Placement = Entity & { + plan_id: Identifier; + student_id: Identifier; student: Student; anchor: boolean; + priority: number; availabilities: Availability[]; } diff --git a/src/components/PlanCard.tsx b/src/components/PlanCard.tsx index a4dfff8..d2c3bef 100644 --- a/src/components/PlanCard.tsx +++ b/src/components/PlanCard.tsx @@ -9,11 +9,12 @@ import { MoreOutlined } from "@ant-design/icons"; import { ConfirmationDialog } from "@digitalaidseattle/mui"; import { Card, CardContent, IconButton, Menu, MenuItem, Theme, Typography } from "@mui/material"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { useNavigate } from "react-router"; import { planService } from "../api/cePlanService"; import { Plan } from "../api/types"; import { useNotifications } from "@digitalaidseattle/core"; +import { placementService } from "../api/cePlacementService"; export const PlanCard = (props: { plan: Plan }) => { @@ -22,10 +23,24 @@ export const PlanCard = (props: { plan: Plan }) => { const [anchorEl, setAnchorEl] = useState(null); const showMenu = Boolean(anchorEl); + const [plan, setPlan] = useState(props.plan); + const [openDeleteDialog, setOpenDeleteDialog] = useState(false); const navigate = useNavigate(); + useEffect(() => { + if (props.plan) { + placementService.findByPlanId(props.plan.id) + .then(placements => { + setPlan({ + ...props.plan, + placements: placements + }) + }) + } + }, [props]); + const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }; @@ -35,12 +50,12 @@ export const PlanCard = (props: { plan: Plan }) => { }; const handleOpen = () => { - navigate(`/plan/${props.plan.id}`); + navigate(`/plan/${plan.id}`); setAnchorEl(null); }; const handleDuplicate = () => { - planService.duplicate(props.plan) + planService.duplicate(plan) setAnchorEl(null); }; @@ -50,17 +65,17 @@ export const PlanCard = (props: { plan: Plan }) => { }; const doDelete = () => { - if (props.plan) { - planService.delete(props.plan.id) + if (plan) { + planService.delete(plan.id) .then(() => { - notifications.success(`Deleted plan ${props.plan.name}.`); + notifications.success(`Deleted plan ${plan.name}.`); setOpenDeleteDialog(false); setAnchorEl(null); }) } }; - return ( + return (plan && { Delete... - {props.plan.name} - Notes : {props.plan.note} - Stats : # of students, groups, etc. + {plan.name} + Notes : {plan.note} + Students : {plan.placements ? plan.placements.length : 0} doDelete()} handleCancel={() => setOpenDeleteDialog(false)} /> diff --git a/src/components/PlanDetails/Setup.tsx b/src/components/PlanDetails/Setup.tsx index 70ec560..f1a545e 100644 --- a/src/components/PlanDetails/Setup.tsx +++ b/src/components/PlanDetails/Setup.tsx @@ -152,7 +152,7 @@ export default function Setup() { { const { id: cohortId } = useParams(); const { refresh, setRefresh } = useContext(RefreshContext); + const { setLoading } = useContext(LoadingContext); const notifications = useNotifications(); @@ -40,7 +41,7 @@ const CohortPage: React.FC = () => { function handleNameChange(newText: string) { if (cohort) { cohortService - .update(cohort.id.toString(), { name: newText }) // FIXME change ID to UUID + .update(cohort.id.toString(), { name: newText }) .then(updated => { setCohort(updated); notifications.success(`Cohort ${updated.name} updated.`); @@ -51,10 +52,13 @@ const CohortPage: React.FC = () => { function handleNewPlan() { if (cohort) { + setLoading(true); planService.create(cohort) .then(plan => { + setRefresh(refresh + 1); notifications.success(`Plan ${plan.name} created.`); }) + .finally(() => setLoading(false)); } } @@ -68,11 +72,10 @@ const CohortPage: React.FC = () => { {plans.map(plan => - + )} - ) }; diff --git a/supabase/migrations/000015_db_placement_updates.sql b/supabase/migrations/000015_db_placement_updates.sql new file mode 100644 index 0000000..81acb3a --- /dev/null +++ b/supabase/migrations/000015_db_placement_updates.sql @@ -0,0 +1,14 @@ +ALTER TABLE placement DROP COLUMN plan_id; +ALTER TABLE placement ADD COLUMN plan_id UUID REFERENCES plan(id); + +ALTER TABLE placement DROP COLUMN student_id; +ALTER TABLE placement ADD COLUMN student_id UUID REFERENCES student(id); + +ALTER TABLE placement ADD PRIMARY KEY (pland_id, student_id); + +ALTER TABLE placement +ADD CONSTRAINT placement_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES plan(id) ON DELETE CASCADE; + +ALTER TABLE placement +ADD CONSTRAINT placement_student_id_fkey FOREIGN KEY (student_id) REFERENCES student(id); + From cf819032598959787119632f6cd2fb3bf968dc65 Mon Sep 17 00:00:00 2001 From: jnakaso Date: Mon, 17 Feb 2025 14:34:11 -0800 Subject: [PATCH 06/10] lint --- src/components/PlanDetails/GroupBoard.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/PlanDetails/GroupBoard.tsx b/src/components/PlanDetails/GroupBoard.tsx index d6c5073..151dd36 100644 --- a/src/components/PlanDetails/GroupBoard.tsx +++ b/src/components/PlanDetails/GroupBoard.tsx @@ -38,7 +38,7 @@ export const GroupBoard = (props: { plan: Plan | undefined }) => { useEffect(() => { if (props.plan) { - setCategories(props.plan.groups.map(group => { + setCategories((props.plan.groups ?? []).map(group => { return { label: group.groupNo, value: group.groupNo } })) } @@ -51,7 +51,7 @@ export const GroupBoard = (props: { plan: Plan | undefined }) => { function isCategory(item: EnrollmentWrapper, category: DDCategory): boolean { if (props.plan) { const group = props.plan.groups.find(group => group.groupNo === category.value); - return group ? group.studentIds.includes(item.studentId) : false; + return group ? group.studentIds.includes(item.student_id as string) : false; } return false; } From 39f7b1b89d9efa845e8a844d9130189b67c54d5b Mon Sep 17 00:00:00 2001 From: jnakaso Date: Thu, 20 Feb 2025 07:10:51 -0800 Subject: [PATCH 07/10] temp disable constraint --- supabase/migrations/000015_db_placement_updates.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/supabase/migrations/000015_db_placement_updates.sql b/supabase/migrations/000015_db_placement_updates.sql index 81acb3a..4bd115f 100644 --- a/supabase/migrations/000015_db_placement_updates.sql +++ b/supabase/migrations/000015_db_placement_updates.sql @@ -9,6 +9,6 @@ ALTER TABLE placement ADD PRIMARY KEY (pland_id, student_id); ALTER TABLE placement ADD CONSTRAINT placement_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES plan(id) ON DELETE CASCADE; -ALTER TABLE placement -ADD CONSTRAINT placement_student_id_fkey FOREIGN KEY (student_id) REFERENCES student(id); +-- ALTER TABLE placement +-- ADD CONSTRAINT placement_student_id_fkey FOREIGN KEY (student_id) REFERENCES student(id); From 037f16c816771c9772dd8deee037794546520237 Mon Sep 17 00:00:00 2001 From: jnakaso Date: Thu, 20 Feb 2025 07:14:05 -0800 Subject: [PATCH 08/10] remove PR migration in dev --- .github/workflows/supabase-pull-request.yml | 39 --------------------- 1 file changed, 39 deletions(-) delete mode 100644 .github/workflows/supabase-pull-request.yml diff --git a/.github/workflows/supabase-pull-request.yml b/.github/workflows/supabase-pull-request.yml deleted file mode 100644 index 58c3351..0000000 --- a/.github/workflows/supabase-pull-request.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Deploy to Supabase on PR - -on: pull_request - -jobs: - deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: '18' # Ensure compatibility with Supabase CLI - - - name: Install dependencies - run: npm install - - - name: Install Supabase CLI locally - run: npm install supabase - - - name: Authenticate Supabase CLI - run: npx supabase login --token "${{ secrets.SUPABASE_ACCESS_TOKEN }}" - - - name: Link Supabase Project - run: npx supabase link --project-ref "${{ secrets.SUPABASE_PROJECT_REF }}" --password "${{ secrets.SUPABASE_DB_PWD }}" - - - name: Deploy migrations - run: npx supabase db push --password "${{ secrets.SUPABASE_DB_PWD }}" --include-all - - - name: Deploy Edge Functions - run: | - if [ -d "supabase/functions" ] && [ "$(ls -A supabase/functions)" ]; then - npx supabase functions deploy - else - echo "No functions to deploy." - fi From 0cb0502e0c9c3e36fca6d2634f388aa1dd4a2b56 Mon Sep 17 00:00:00 2001 From: jnakaso Date: Thu, 20 Feb 2025 07:55:18 -0800 Subject: [PATCH 09/10] plans have placements --- src/components/PlanDetails/GroupBoard.tsx | 28 ++++++++++---------- src/components/PlanDetails/SetupStudents.tsx | 13 ++++----- src/utils/props.ts | 4 ++- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/components/PlanDetails/GroupBoard.tsx b/src/components/PlanDetails/GroupBoard.tsx index 4216465..22f43bd 100644 --- a/src/components/PlanDetails/GroupBoard.tsx +++ b/src/components/PlanDetails/GroupBoard.tsx @@ -17,33 +17,33 @@ import { Stack, Typography } from "@mui/material"; -import { DragAndDrop, DDCategory, DDType } from '@digitalaidseattle/draganddrop'; -import { Placement, Plan } from "../../api/types"; +import { Placement } from "../../api/types"; +import { PlanProps } from "../../utils/props"; -export const StudentCard = (props: { enrollment: Enrollment }) => { - const anchor = props.enrollment.anchor ? 'green' : 'gray;' - const priority = props.enrollment.priority ? 'green' : 'gray;' +export const StudentCard = (props: { placement: Placement }) => { + const anchor = props.placement.anchor ? 'green' : 'gray;' + const priority = props.placement.priority ? 'green' : 'gray;' return ( - {props.enrollment.anchor && + {props.placement.anchor && } - {props.enrollment.priority && + {props.placement.priority && } - {props.enrollment.student.name} + {props.placement.student.name} ); } -type EnrollmentWrapper = Placement & DDType +type PlacementWrapper = Placement & DDType export const GroupBoard: React.FC = ({ plan }) => { const [categories, setCategories] = useState[]>(); @@ -58,15 +58,15 @@ export const GroupBoard: React.FC = ({ plan }) => { console.log(c, t) } - function isCategory(item: EnrollmentWrapper, category: DDCategory): boolean { + function isCategory(item: PlacementWrapper, category: DDCategory): boolean { if (plan) { const group = plan.groups.find(group => group.groupNo === category.value); - return group ? group.studentIds.includes(item.studentId) : false; + return group ? group.studentIds.includes(item.student_id) : false; } return false; } - function cellRender(item: EnrollmentWrapper): ReactNode { + function cellRender(item: PlacementWrapper): ReactNode { return } @@ -75,8 +75,8 @@ export const GroupBoard: React.FC = ({ plan }) => { <>{plan && categories && , e: Enrollment) => handleChange(c, e)} - items={plan.enrollments} + onChange={(c: Map, e: Placement) => handleChange(c, e)} + items={plan.placements} categories={categories} isCategory={isCategory} cardRenderer={cellRender} diff --git a/src/components/PlanDetails/SetupStudents.tsx b/src/components/PlanDetails/SetupStudents.tsx index e586564..b1b237c 100644 --- a/src/components/PlanDetails/SetupStudents.tsx +++ b/src/components/PlanDetails/SetupStudents.tsx @@ -27,6 +27,7 @@ import { import { ExclamationCircleFilled, StarFilled } from '@ant-design/icons'; import { PageInfo } from '@digitalaidseattle/supabase'; import { PlanProps } from '../../utils/props'; +import { Student } from '../../api/types'; const PAGE_SIZE = 10; @@ -42,13 +43,13 @@ export const SetupStudents: React.FC = ({ plan }) => { const [pageInfo, setPageInfo] = useState>({ rows: [], totalRowCount: 0 }); useEffect(() => { - const enrolledStudents = plan.enrollments.map(en => { + const enrolledStudents = plan.placements.map(placement => { return { - ...en.student, - enrollmentId: en.id, - anchor: en.anchor, - priority: en.priority, - } + ...placement.student, + enrollmentId: placement.id, + anchor: placement.anchor, + priority: placement.priority, + } as EnrolledStudent }); setPageInfo({ rows: enrolledStudents, diff --git a/src/utils/props.ts b/src/utils/props.ts index 2afa1ce..a43576a 100644 --- a/src/utils/props.ts +++ b/src/utils/props.ts @@ -1,10 +1,12 @@ /** - * App.tsx + * props.ts * * @copyright 2025 Digital Aid Seattle * */ +import { Plan } from "../api/types"; + export interface PlanProps { plan: Plan; onChange?: (plan: Plan) => void; From 2edebabe65f73943418d7cdebf210cb8af4cc097 Mon Sep 17 00:00:00 2001 From: jnakaso Date: Thu, 20 Feb 2025 09:05:28 -0800 Subject: [PATCH 10/10] remove migration on PRs --- .github/workflows/supabase-pull-request.yml | 39 --------------------- 1 file changed, 39 deletions(-) delete mode 100644 .github/workflows/supabase-pull-request.yml diff --git a/.github/workflows/supabase-pull-request.yml b/.github/workflows/supabase-pull-request.yml deleted file mode 100644 index 58c3351..0000000 --- a/.github/workflows/supabase-pull-request.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Deploy to Supabase on PR - -on: pull_request - -jobs: - deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: '18' # Ensure compatibility with Supabase CLI - - - name: Install dependencies - run: npm install - - - name: Install Supabase CLI locally - run: npm install supabase - - - name: Authenticate Supabase CLI - run: npx supabase login --token "${{ secrets.SUPABASE_ACCESS_TOKEN }}" - - - name: Link Supabase Project - run: npx supabase link --project-ref "${{ secrets.SUPABASE_PROJECT_REF }}" --password "${{ secrets.SUPABASE_DB_PWD }}" - - - name: Deploy migrations - run: npx supabase db push --password "${{ secrets.SUPABASE_DB_PWD }}" --include-all - - - name: Deploy Edge Functions - run: | - if [ -d "supabase/functions" ] && [ "$(ls -A supabase/functions)" ]; then - npx supabase functions deploy - else - echo "No functions to deploy." - fi