From 992aaf5a4308fc506673e34168dcf856bb9d8836 Mon Sep 17 00:00:00 2001 From: neelesh Date: Thu, 20 Nov 2025 23:22:59 -0800 Subject: [PATCH 01/40] created tickets.sql and basic ticket schema, access policies still needed --- package-lock.json | 6 +++--- supabase/schemas/tickets.sql | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 supabase/schemas/tickets.sql diff --git a/package-lock.json b/package-lock.json index a7a5582..d6bd7f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4685,9 +4685,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/supabase/schemas/tickets.sql b/supabase/schemas/tickets.sql new file mode 100644 index 0000000..dcc5a42 --- /dev/null +++ b/supabase/schemas/tickets.sql @@ -0,0 +1,15 @@ +create table "tickets"{ + "ticket_id" SERIAL, -- SERIAL is an int that create unique sequential values?? + "requestor_user_id" INT, + "store_id" INT, + "status" VARCHAR(50), -- max 50 character status + "date_submitted" TIMESTAMP WITH TIME ZONE, + PRIMARY KEY (ticket_id), + FOREIGN KEY (requestor_user_id) REFERENCES users(user_id), + FOREIGN KEY (store_id) REFERENCES stores(store_id) +} + +alter table tickets enable row level security; + +-- create the policies here, idk enough sql rn to do so. Also we have to delete these comments lol + From c9b6e201bd6f39d3765c34e9f49292c474e6aad1 Mon Sep 17 00:00:00 2001 From: katieyungchung Date: Mon, 24 Nov 2025 21:38:37 -0800 Subject: [PATCH 02/40] donations sql table --- supabase/schemas/donations.sql | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 supabase/schemas/donations.sql diff --git a/supabase/schemas/donations.sql b/supabase/schemas/donations.sql new file mode 100644 index 0000000..8b09437 --- /dev/null +++ b/supabase/schemas/donations.sql @@ -0,0 +1,24 @@ +/* +- Create the donations table according to schema described +- Write RLS (row level security) policies following supabase/schemas/example.sql +that enable unauthenticated users to view/update the table +*/ + +create table donations ( + donation_id uuid default uuid_generate_v4() primary key, -- generated by default + receiver_user_id uuid not null references users(id), -- linked to users table, required + store_id uuid references stores(id), -- linked to stores table, optional + date_submitted timestamp with time zone default current_timestamp, -- generated by default + donor_is_individual boolean not null, -- optional, used if donor is an individual + donor_individual_name text, -- optional, used if donor is an individual + donor_business_name text, -- optional, used if donor is a business + donor_business_contact_name text, -- optional, used if donor is a business + donor_email varchar(255), -- optional, can't store more than 255 characters + donor_phone varchar(20), -- optional, can't store more than 20 characters + donor_street_address boolean not null, -- required, whether or not donor wants to receive mailings + donor_receive_mailings boolean not null, -- required, whether or not donor wants to receive mailings + donor_receive_emails boolean not null, -- required, whether or not donor wants to receive emails + donor_remain_anonymous boolean not null, -- required, whether or not donor wants to remain anonymous + estimated_value numeric not null, -- required, estimated value of donation in USD + items_donated text not null -- required, free-text description of items +); \ No newline at end of file From 11a10a28469bec2b7ebec883cdc6044365c5dfc6 Mon Sep 17 00:00:00 2001 From: katieyungchung Date: Mon, 24 Nov 2025 22:36:12 -0800 Subject: [PATCH 03/40] add rls policies --- package-lock.json | 6 +- .../20251125063314_create_donations_table.sql | 106 ++++++++++++++++++ supabase/schemas/donations.sql | 33 +++++- 3 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 supabase/migrations/20251125063314_create_donations_table.sql diff --git a/package-lock.json b/package-lock.json index a7a5582..3cef715 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6376,9 +6376,9 @@ } }, "node_modules/supabase": { - "version": "2.58.5", - "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.58.5.tgz", - "integrity": "sha512-mYZSkUIePTdmwlHd26Pff8wpmjfre8gcuWzrc5QqhZgZvCXugVzAQQhcjaQisw5kusbPQWNIjUwcHYEKmejhPw==", + "version": "2.61.2", + "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.61.2.tgz", + "integrity": "sha512-dSmCHErwEQrpSQb1PKzpwZqhEl8qj8KVzVtqJBcKROmPOeWZ9CAghxW5uX2B1bGKPjVXAN6MA4rP/Mr7+qmj1A==", "dev": true, "hasInstallScript": true, "license": "MIT", diff --git a/supabase/migrations/20251125063314_create_donations_table.sql b/supabase/migrations/20251125063314_create_donations_table.sql new file mode 100644 index 0000000..27f39be --- /dev/null +++ b/supabase/migrations/20251125063314_create_donations_table.sql @@ -0,0 +1,106 @@ + + create table "public"."donations" ( + "donation_id" text not null default (gen_random_uuid())::text, + "receiver_user_id" text not null, + "store_id" text, + "date_submitted" timestamp with time zone default CURRENT_TIMESTAMP, + "donor_is_individual" boolean not null, + "donor_individual_name" text, + "donor_business_name" text, + "donor_business_contact_name" text, + "donor_email" character varying(255), + "donor_phone" character varying(20), + "donor_street_address" boolean not null, + "donor_receive_mailings" boolean not null, + "donor_receive_emails" boolean not null, + "donor_remain_anonymous" boolean not null, + "estimated_value" numeric not null, + "items_donated" text not null + ); + + +alter table "public"."donations" enable row level security; + +CREATE UNIQUE INDEX donations_pkey ON public.donations USING btree (donation_id); + +alter table "public"."donations" add constraint "donations_pkey" PRIMARY KEY using index "donations_pkey"; + +grant delete on table "public"."donations" to "anon"; + +grant insert on table "public"."donations" to "anon"; + +grant references on table "public"."donations" to "anon"; + +grant select on table "public"."donations" to "anon"; + +grant trigger on table "public"."donations" to "anon"; + +grant truncate on table "public"."donations" to "anon"; + +grant update on table "public"."donations" to "anon"; + +grant delete on table "public"."donations" to "authenticated"; + +grant insert on table "public"."donations" to "authenticated"; + +grant references on table "public"."donations" to "authenticated"; + +grant select on table "public"."donations" to "authenticated"; + +grant trigger on table "public"."donations" to "authenticated"; + +grant truncate on table "public"."donations" to "authenticated"; + +grant update on table "public"."donations" to "authenticated"; + +grant delete on table "public"."donations" to "service_role"; + +grant insert on table "public"."donations" to "service_role"; + +grant references on table "public"."donations" to "service_role"; + +grant select on table "public"."donations" to "service_role"; + +grant trigger on table "public"."donations" to "service_role"; + +grant truncate on table "public"."donations" to "service_role"; + +grant update on table "public"."donations" to "service_role"; + + + create policy "public can delete entries in donations" + on "public"."donations" + as permissive + for delete + to anon +using (true); + + + + create policy "public can insert entries in donations" + on "public"."donations" + as permissive + for insert + to anon +with check (true); + + + + create policy "public can read entries in donations" + on "public"."donations" + as permissive + for select + to anon +using (true); + + + + create policy "public can update entries in donations" + on "public"."donations" + as permissive + for update + to anon +with check (true); + + + diff --git a/supabase/schemas/donations.sql b/supabase/schemas/donations.sql index 8b09437..8f17ec2 100644 --- a/supabase/schemas/donations.sql +++ b/supabase/schemas/donations.sql @@ -6,8 +6,8 @@ that enable unauthenticated users to view/update the table create table donations ( donation_id uuid default uuid_generate_v4() primary key, -- generated by default - receiver_user_id uuid not null references users(id), -- linked to users table, required - store_id uuid references stores(id), -- linked to stores table, optional + receiver_user_id uuid not null, -- references users(id), -- linked to users table, required + store_id uuid, -- references stores(id), -- linked to stores table, optional date_submitted timestamp with time zone default current_timestamp, -- generated by default donor_is_individual boolean not null, -- optional, used if donor is an individual donor_individual_name text, -- optional, used if donor is an individual @@ -21,4 +21,31 @@ create table donations ( donor_remain_anonymous boolean not null, -- required, whether or not donor wants to remain anonymous estimated_value numeric not null, -- required, estimated value of donation in USD items_donated text not null -- required, free-text description of items -); \ No newline at end of file +); + +-- write RLS policies that enable select, insert, update, and delete across all unauthenticated users +alter table donations enable row level security; + +-- select +create policy "public can read entries in donations" +on public.donations +for select to anon -- applies to anon role (anonymous/public users) +using (true); -- all rows satisfy condition, all rows are readable + +-- insert +create policy "public can insert entries in donations" +on public.donations +for insert to anon -- applies to anon role (anonymous/public users) +with check (true); -- for insert/update row validation, only rows where condition evaluates to true can be inserted/updated + +-- update +create policy "public can update entries in donations" +on public.donations +for update to anon +with check(true); + +-- delete +create policy "public can delete entries in donations" +on public.donations +for delete to anon +using (true); From 5fc8071ceca73494de2d58d56799005d61623fcb Mon Sep 17 00:00:00 2001 From: Camillalalala Date: Mon, 24 Nov 2025 22:41:00 -0800 Subject: [PATCH 04/40] Created inventory_items table, migration file, and inventory_item type --- app/api/inventory-items/actions.ts | 0 app/types/InventoryItem.ts | 15 +++ ...125063858_create_inventory_items_table.sql | 100 ++++++++++++++++++ supabase/schemas/inventory_items.sql | 34 ++++++ 4 files changed, 149 insertions(+) create mode 100644 app/api/inventory-items/actions.ts create mode 100644 app/types/InventoryItem.ts create mode 100644 supabase/migrations/20251125063858_create_inventory_items_table.sql create mode 100644 supabase/schemas/inventory_items.sql diff --git a/app/api/inventory-items/actions.ts b/app/api/inventory-items/actions.ts new file mode 100644 index 0000000..e69de29 diff --git a/app/types/InventoryItem.ts b/app/types/InventoryItem.ts new file mode 100644 index 0000000..206b4bd --- /dev/null +++ b/app/types/InventoryItem.ts @@ -0,0 +1,15 @@ +// Create a type InventoryItem corresponding the table. +// For the uuid PostgreSQL data type, you can use a TypeScrypt string. +import { UUID } from "crypto" + +export type IventoryType = { + inventory_item_id: string; // UUID + name: Text; + category: string; + subcategory: string; + item: string; + description: Text; + photo_url: Text; + quantity_available: number; + is_hidden: boolean; +}; \ No newline at end of file diff --git a/supabase/migrations/20251125063858_create_inventory_items_table.sql b/supabase/migrations/20251125063858_create_inventory_items_table.sql new file mode 100644 index 0000000..1573cb1 --- /dev/null +++ b/supabase/migrations/20251125063858_create_inventory_items_table.sql @@ -0,0 +1,100 @@ + + create table "public"."inventory_items" ( + "inventory_item_id" uuid not null default extensions.uuid_generate_v4(), + "store_id" uuid not null, + "category" character varying(50) not null, + "subcategory" character varying(50) not null, + "item" character varying(255) not null, + "description" text not null, + "photo_url" text, + "quantity_available" integer not null, + "is_hidden" boolean not null + ); + + +alter table "public"."inventory_items" enable row level security; + +CREATE UNIQUE INDEX inventory_items_pkey ON public.inventory_items USING btree (inventory_item_id); + +alter table "public"."inventory_items" add constraint "inventory_items_pkey" PRIMARY KEY using index "inventory_items_pkey"; + +grant delete on table "public"."inventory_items" to "anon"; + +grant insert on table "public"."inventory_items" to "anon"; + +grant references on table "public"."inventory_items" to "anon"; + +grant select on table "public"."inventory_items" to "anon"; + +grant trigger on table "public"."inventory_items" to "anon"; + +grant truncate on table "public"."inventory_items" to "anon"; + +grant update on table "public"."inventory_items" to "anon"; + +grant delete on table "public"."inventory_items" to "authenticated"; + +grant insert on table "public"."inventory_items" to "authenticated"; + +grant references on table "public"."inventory_items" to "authenticated"; + +grant select on table "public"."inventory_items" to "authenticated"; + +grant trigger on table "public"."inventory_items" to "authenticated"; + +grant truncate on table "public"."inventory_items" to "authenticated"; + +grant update on table "public"."inventory_items" to "authenticated"; + +grant delete on table "public"."inventory_items" to "service_role"; + +grant insert on table "public"."inventory_items" to "service_role"; + +grant references on table "public"."inventory_items" to "service_role"; + +grant select on table "public"."inventory_items" to "service_role"; + +grant trigger on table "public"."inventory_items" to "service_role"; + +grant truncate on table "public"."inventory_items" to "service_role"; + +grant update on table "public"."inventory_items" to "service_role"; + + + create policy "public can delete entries in inventory_items" + on "public"."inventory_items" + as permissive + for delete + to anon +using (true); + + + + create policy "public can insert entries in inventory_items" + on "public"."inventory_items" + as permissive + for insert + to anon +with check (true); + + + + create policy "public can select entries in inventory_items" + on "public"."inventory_items" + as permissive + for select + to anon +using (true); + + + + create policy "public can update entries in inventory_items" + on "public"."inventory_items" + as permissive + for update + to anon +using (true) +with check (true); + + + diff --git a/supabase/schemas/inventory_items.sql b/supabase/schemas/inventory_items.sql new file mode 100644 index 0000000..c36ed61 --- /dev/null +++ b/supabase/schemas/inventory_items.sql @@ -0,0 +1,34 @@ +create table "inventory_items" ( + "inventory_item_id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + "store_id" UUID not null, + "category" varchar(50) not null, + "subcategory" varchar(50) not null, + "item" varchar(255) not null, + "description" text not null, + "photo_url" text, + "quantity_available" int not null, + "is_hidden" boolean not null +); + +alter table "inventory_items" enable row level security; + +create policy "public can select entries in inventory_items" +on public.inventory_items +for select to anon +using (true); + +create policy "public can insert entries in inventory_items" +on public.inventory_items +for insert to anon +with check (true); + +create policy "public can update entries in inventory_items" +on public.inventory_items +for update to anon +using (true) +with check (true); + +create policy "public can delete entries in inventory_items" +on public.inventory_items +for delete to anon +using (true); \ No newline at end of file From 2d72b04d040178d30eadfc2c0773785519b2b56f Mon Sep 17 00:00:00 2001 From: 1004yeeun Date: Mon, 24 Nov 2025 22:50:23 -0800 Subject: [PATCH 05/40] Didn't finish server actions --- app/api/inventory-items/actions.ts | 34 ++++++++++++++++++++++++++++++ app/types/InventoryItem.ts | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/app/api/inventory-items/actions.ts b/app/api/inventory-items/actions.ts index e69de29..f5ee952 100644 --- a/app/api/inventory-items/actions.ts +++ b/app/api/inventory-items/actions.ts @@ -0,0 +1,34 @@ +"use server"; + +import { InventoryType } from "@/app/types/InventoryItem"; +import { createClient } from "@/app/lib/supabase/server-client"; + +export const createItem = async (data: InventoryType) => { + const supabase = await createClient(); + const { error } = await supabase.from("inventory_items").insert(data); + + if (error) { + throw error; + } + return entry; +} + +export const changeItemQuantity = async (data: InventoryType) => { + const supabase = await createClient(); + const { data: entry, error } = await supabase.from("example").insert(data); + + if (error) { + throw error; + } + return entry; +} + +export const deleteItem = async (data: InventoryType) => { + const supabase = await createClient(); + const { data: entry, error } = await supabase.from("example").insert(data); + + if (error) { + throw error; + } + return entry; + } \ No newline at end of file diff --git a/app/types/InventoryItem.ts b/app/types/InventoryItem.ts index 206b4bd..6c30f16 100644 --- a/app/types/InventoryItem.ts +++ b/app/types/InventoryItem.ts @@ -2,7 +2,7 @@ // For the uuid PostgreSQL data type, you can use a TypeScrypt string. import { UUID } from "crypto" -export type IventoryType = { +export type InventoryType = { inventory_item_id: string; // UUID name: Text; category: string; From 811a3e980d8381b3f4883c23d2c586be0c50914c Mon Sep 17 00:00:00 2001 From: neelesh Date: Sat, 29 Nov 2025 11:02:18 -0800 Subject: [PATCH 06/40] Finished Schema, began server actions Co-authored-by: Raymond Kao --- app/api/tickets/actions.ts | 9 ++ app/types/Ticket.ts | 9 ++ .../20251129181914_create_tickets_table.sql | 96 +++++++++++++++++++ supabase/schemas/tickets.sql | 40 +++++--- supabase/seeds/{example.sql => tickets.sql} | 2 +- 5 files changed, 144 insertions(+), 12 deletions(-) create mode 100644 app/api/tickets/actions.ts create mode 100644 app/types/Ticket.ts create mode 100644 supabase/migrations/20251129181914_create_tickets_table.sql rename supabase/seeds/{example.sql => tickets.sql} (71%) diff --git a/app/api/tickets/actions.ts b/app/api/tickets/actions.ts new file mode 100644 index 0000000..09ac900 --- /dev/null +++ b/app/api/tickets/actions.ts @@ -0,0 +1,9 @@ +"use server"; // directive that indicates that the functions we define are server actions + +import { createClient } from "@/app/lib/supabase/server-client"; + +/*export async function createTicket(formData) { + const supabase = await createClient(); + // pulling out the error from the insert into tickets table + const { error: err } = await supabase.from('tickets').insert({ticket_id: }) +}*/ diff --git a/app/types/Ticket.ts b/app/types/Ticket.ts new file mode 100644 index 0000000..3d16954 --- /dev/null +++ b/app/types/Ticket.ts @@ -0,0 +1,9 @@ +export type Ticket = { + ticket_id: string; // string corresponds to uuid + requestor_user_id: string; + store_id: string; + status: string; // string corresponds to varchar + date_submitted: Date | string; // Date | string corresponds to time stamp with time zone +}; + +export type TicketUpdate = Partial; diff --git a/supabase/migrations/20251129181914_create_tickets_table.sql b/supabase/migrations/20251129181914_create_tickets_table.sql new file mode 100644 index 0000000..5a06920 --- /dev/null +++ b/supabase/migrations/20251129181914_create_tickets_table.sql @@ -0,0 +1,96 @@ + + create table "public"."tickets" ( + "ticket_id" uuid not null default extensions.uuid_generate_v4(), + "requestor_user_id" uuid not null, + "store_id" uuid not null, + "status" character varying(50) not null, + "date_submitted" timestamp with time zone default now() + ); + + +alter table "public"."tickets" enable row level security; + +CREATE UNIQUE INDEX tickets_pkey ON public.tickets USING btree (ticket_id); + +alter table "public"."tickets" add constraint "tickets_pkey" PRIMARY KEY using index "tickets_pkey"; + +grant delete on table "public"."tickets" to "anon"; + +grant insert on table "public"."tickets" to "anon"; + +grant references on table "public"."tickets" to "anon"; + +grant select on table "public"."tickets" to "anon"; + +grant trigger on table "public"."tickets" to "anon"; + +grant truncate on table "public"."tickets" to "anon"; + +grant update on table "public"."tickets" to "anon"; + +grant delete on table "public"."tickets" to "authenticated"; + +grant insert on table "public"."tickets" to "authenticated"; + +grant references on table "public"."tickets" to "authenticated"; + +grant select on table "public"."tickets" to "authenticated"; + +grant trigger on table "public"."tickets" to "authenticated"; + +grant truncate on table "public"."tickets" to "authenticated"; + +grant update on table "public"."tickets" to "authenticated"; + +grant delete on table "public"."tickets" to "service_role"; + +grant insert on table "public"."tickets" to "service_role"; + +grant references on table "public"."tickets" to "service_role"; + +grant select on table "public"."tickets" to "service_role"; + +grant trigger on table "public"."tickets" to "service_role"; + +grant truncate on table "public"."tickets" to "service_role"; + +grant update on table "public"."tickets" to "service_role"; + + + create policy "public can delete entries in tickets" + on "public"."tickets" + as permissive + for delete + to anon +using (true); + + + + create policy "public can insert entries in tickets" + on "public"."tickets" + as permissive + for insert + to anon +with check (true); + + + + create policy "public can read entries in tickets" + on "public"."tickets" + as permissive + for select + to anon +using (true); + + + + create policy "public can update entries in tickets" + on "public"."tickets" + as permissive + for update + to anon +using (true) +with check (true); + + + diff --git a/supabase/schemas/tickets.sql b/supabase/schemas/tickets.sql index dcc5a42..d831b42 100644 --- a/supabase/schemas/tickets.sql +++ b/supabase/schemas/tickets.sql @@ -1,15 +1,33 @@ -create table "tickets"{ - "ticket_id" SERIAL, -- SERIAL is an int that create unique sequential values?? - "requestor_user_id" INT, - "store_id" INT, - "status" VARCHAR(50), -- max 50 character status - "date_submitted" TIMESTAMP WITH TIME ZONE, - PRIMARY KEY (ticket_id), - FOREIGN KEY (requestor_user_id) REFERENCES users(user_id), - FOREIGN KEY (store_id) REFERENCES stores(store_id) -} +create table "tickets" ( + "ticket_id" uuid default uuid_generate_v4() primary key, + "requestor_user_id" uuid not null, + "store_id" uuid not null, + "status" VARCHAR(50) not null, -- max 50 character status + "date_submitted" TIMESTAMP WITH TIME ZONE default now() + -- FOREIGN KEY (requestor_user_id) REFERENCES users(user_id), + -- FOREIGN KEY (store_id) REFERENCES stores(store_id) +); alter table tickets enable row level security; --- create the policies here, idk enough sql rn to do so. Also we have to delete these comments lol +create policy "public can read entries in tickets" +on public.tickets +for select to anon +using (true); + +create policy "public can insert entries in tickets" +on public.tickets +for insert to anon +with check (true); + +create policy "public can update entries in tickets" +on public.tickets +for update to anon +using (true) +with check (true); + +create policy "public can delete entries in tickets" +on public.tickets +for delete to anon +using (true); diff --git a/supabase/seeds/example.sql b/supabase/seeds/tickets.sql similarity index 71% rename from supabase/seeds/example.sql rename to supabase/seeds/tickets.sql index 16af5b8..b739183 100644 --- a/supabase/seeds/example.sql +++ b/supabase/seeds/tickets.sql @@ -1,4 +1,4 @@ -insert into example +insert into tickets (id, name) values (1, 'alice'), From a1ee6b2193a5d8d0aa81d872947a70f5ee7bd51b Mon Sep 17 00:00:00 2001 From: minado05 Date: Mon, 1 Dec 2025 22:31:41 -0800 Subject: [PATCH 07/40] updates to store setup --- app/(pages)/example/page.tsx | 36 +++++--- app/api/example/stores/action.ts | 27 ++++++ app/types/Store.ts | 5 ++ .../20251125060428_create_stores_table.sql | 90 +++++++++++++++++++ supabase/schemas/stores.sql | 30 +++++++ 5 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 app/api/example/stores/action.ts create mode 100644 app/types/Store.ts create mode 100644 supabase/migrations/20251125060428_create_stores_table.sql create mode 100644 supabase/schemas/stores.sql diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index bffadf2..4a7e8f0 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -1,25 +1,39 @@ "use client"; import { createExampleEntry } from "@/app/api/example/actions"; +import { createStore } from "@/app/api/example/stores/action"; import { ExampleType } from "@/app/types/ExampleType"; +import { Store } from "@/app/types/Store"; +import { deleteStore } from "@/app/api/example/stores/action"; export default function Example() { const data: ExampleType = { id: 5, - name: 'harry', + name: "harry", + }; + + const storeData: Store = { + store_id: "123", + name: "test store", + street_address: "test address", }; const handleExampleClick = async () => { await createExampleEntry(data); }; - return ( -
- Welcome to PATH! This is an example page. -
- -
- ); -} \ No newline at end of file + const handleAddStoreClick = async () => { + await createStore(storeData); + }; + + const handleDeleteStoreClick = async () => { + await deleteStore("123"); + }; +
+ Welcome to PATH! This is an example page. +
+ + + +
; +} diff --git a/app/api/example/stores/action.ts b/app/api/example/stores/action.ts new file mode 100644 index 0000000..04b19b4 --- /dev/null +++ b/app/api/example/stores/action.ts @@ -0,0 +1,27 @@ +"use server"; +import { createClient } from "@/app/lib/supabase/server-client"; +import { Store } from "@/app/types/Store"; + +// create store +export const createStore = async (data: Store) => { + const supabase = await createClient(); + const { data: entry, error } = await supabase.from("stores").insert(data); + if (error) { + throw error; + } + return entry; +}; + +// action deleteStore +// delete store given store_id +export const deleteStore = async (store_id: string) => { + const supabase = await createClient(); + const { data: deletedData, error } = await supabase.from("stores").delete().eq(store_id, 1); + + if (error) { + throw error; + } + return deletedData; +}; + + diff --git a/app/types/Store.ts b/app/types/Store.ts new file mode 100644 index 0000000..d81a8c5 --- /dev/null +++ b/app/types/Store.ts @@ -0,0 +1,5 @@ +export type Store = { + store_id: string; + name: string; + street_address: string; +}; diff --git a/supabase/migrations/20251125060428_create_stores_table.sql b/supabase/migrations/20251125060428_create_stores_table.sql new file mode 100644 index 0000000..33595ec --- /dev/null +++ b/supabase/migrations/20251125060428_create_stores_table.sql @@ -0,0 +1,90 @@ + + create table "public"."stores" ( + "store_id" text, + "name" text, + "street_address" text + ); + + +alter table "public"."stores" enable row level security; + +grant delete on table "public"."stores" to "anon"; + +grant insert on table "public"."stores" to "anon"; + +grant references on table "public"."stores" to "anon"; + +grant select on table "public"."stores" to "anon"; + +grant trigger on table "public"."stores" to "anon"; + +grant truncate on table "public"."stores" to "anon"; + +grant update on table "public"."stores" to "anon"; + +grant delete on table "public"."stores" to "authenticated"; + +grant insert on table "public"."stores" to "authenticated"; + +grant references on table "public"."stores" to "authenticated"; + +grant select on table "public"."stores" to "authenticated"; + +grant trigger on table "public"."stores" to "authenticated"; + +grant truncate on table "public"."stores" to "authenticated"; + +grant update on table "public"."stores" to "authenticated"; + +grant delete on table "public"."stores" to "service_role"; + +grant insert on table "public"."stores" to "service_role"; + +grant references on table "public"."stores" to "service_role"; + +grant select on table "public"."stores" to "service_role"; + +grant trigger on table "public"."stores" to "service_role"; + +grant truncate on table "public"."stores" to "service_role"; + +grant update on table "public"."stores" to "service_role"; + + + create policy "public can delete entries in stores" + on "public"."stores" + as permissive + for delete + to anon +using (true); + + + + create policy "public can insert entries in stores" + on "public"."stores" + as permissive + for insert + to anon +with check (true); + + + + create policy "public can read entries in stores" + on "public"."stores" + as permissive + for select + to anon +using (true); + + + + create policy "public can update entries in stores" + on "public"."stores" + as permissive + for update + to anon +using (true) +with check (true); + + + diff --git a/supabase/schemas/stores.sql b/supabase/schemas/stores.sql new file mode 100644 index 0000000..80db824 --- /dev/null +++ b/supabase/schemas/stores.sql @@ -0,0 +1,30 @@ +create table "stores" ( + store_id text, + name text, + street_address text +); + +alter table "stores" enable row level security; + +create policy "public can read entries in stores" +on public.stores +for select to anon +using (true); + +create policy "public can insert entries in stores" +on public.stores +for insert to anon +with check (true); + +create policy "public can update entries in stores" +on public.stores +for update to anon +using (true) +with check (true); + +create policy "public can delete entries in stores" +on public.stores +for delete to anon +using (true); + + From 31e60184350f20f7de874ff8647e634d443799bb Mon Sep 17 00:00:00 2001 From: katieyungchung Date: Mon, 1 Dec 2025 22:52:23 -0800 Subject: [PATCH 08/40] add server actions and test-donations page --- app/(pages)/test-donations/page.tsx | 49 ++++++++ app/api/donations/actions.ts | 42 +++++++ app/types/Donation.ts | 29 +++++ .../20251202055330_remote_schema.sql | 57 ++++++++++ .../20251202064747_fix_donations_type.sql | 106 ++++++++++++++++++ supabase/schemas/donations.sql | 4 +- 6 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 app/(pages)/test-donations/page.tsx create mode 100644 app/api/donations/actions.ts create mode 100644 app/types/Donation.ts create mode 100644 supabase/migrations/20251202055330_remote_schema.sql create mode 100644 supabase/migrations/20251202064747_fix_donations_type.sql diff --git a/app/(pages)/test-donations/page.tsx b/app/(pages)/test-donations/page.tsx new file mode 100644 index 0000000..e12d11e --- /dev/null +++ b/app/(pages)/test-donations/page.tsx @@ -0,0 +1,49 @@ +"use client"; + +import { createDonation, deleteDonation } from "@/app/api/donations/actions"; + +export default function TestDonationsPage() { + + const handleCreate = async () => { + try { + const donation = await createDonation({ + receiver_user_id: "00000000-0000-0000-0000-000000000000", + store_id: null, + donor_is_individual: true, + donor_individual_name: "Test User", + donor_business_name: "Donor Business Name", + donor_business_contact_name: "Ms. Donor", + donor_email: "test@example.com", + donor_phone: "123-456-7890", + donor_street_address: "1234 Bruin Ave", + donor_receive_mailings: false, + donor_receive_emails: true, + donor_remain_anonymous: false, + estimated_value: 50, + items_donated: "Items donated!" + }); + + console.log("Created donation:", donation); + } catch (err) { + console.error("Create failed:", err); + } + }; + + const handleDelete = async () => { + const idToDelete = "b44b3b18-c499-4aec-9e73-aab1d3788d59"; // hard-coded ID for now + try { + const result = await deleteDonation(idToDelete); + console.log("Deleted donation:", result); + } catch (err) { + console.error("Delete failed:", err); + } + }; + + return ( +
+

Test Donations

+ + +
+ ); +} diff --git a/app/api/donations/actions.ts b/app/api/donations/actions.ts new file mode 100644 index 0000000..24ba958 --- /dev/null +++ b/app/api/donations/actions.ts @@ -0,0 +1,42 @@ +"use server"; + +import { DonationType } from '@/app/types/Donation'; +import { createClient } from '@/app/lib/supabase/server-client'; + +export async function createDonation(data: DonationType) { + const supabase = await createClient(); + + // remove fields that are auto-generated by database + const { donation_id, date_submitted, ...cleanedData } = data; + + const { data: inserted, error } = await supabase + .from("donations") + .insert(cleanedData) + .select() // after inserting, return full new row + .single(); // expect just one row, if more or less, throw an error + + if (error) { + console.error("Error creating donation: ", error); + throw new Error(error.message); + } + + return inserted; // return the row that was created +} + +// deletes donation based on donation_id +export async function deleteDonation(donation_id: string) { + const supabase = await createClient(); + + const { error } = await supabase + .from("donations") + .delete() + .eq("donation_id", donation_id); + + if (error) { + console.error("Error deleting donation:", error); + throw new Error(error.message); + } + + return { success: true }; +} + diff --git a/app/types/Donation.ts b/app/types/Donation.ts new file mode 100644 index 0000000..4bea08e --- /dev/null +++ b/app/types/Donation.ts @@ -0,0 +1,29 @@ +/* +TODO: +- Create a Donation type corresponding to the table +- For uuid postgresql data type, use string +*/ + +export type DonationType = { + donation_id?: string; // optional, generated by database + receiver_user_id: string; + store_id?: string | null; + + date_submitted?: Date | string; // generated by default + donor_is_individual: boolean; + + donor_individual_name?: string | null; // optional + donor_business_name?: string | null; // optional + donor_business_contact_name?: string | null; // optional + + donor_email?: string | null; // optional + donor_phone?: string | null; // optional + + donor_street_address: string | null; // optional + donor_receive_mailings: boolean; + donor_receive_emails: boolean; + donor_remain_anonymous: boolean; + + estimated_value: number; + items_donated: string; +}; \ No newline at end of file diff --git a/supabase/migrations/20251202055330_remote_schema.sql b/supabase/migrations/20251202055330_remote_schema.sql new file mode 100644 index 0000000..375c089 --- /dev/null +++ b/supabase/migrations/20251202055330_remote_schema.sql @@ -0,0 +1,57 @@ +drop policy "public can delete entries in donations" on "public"."donations"; + +drop policy "public can insert entries in donations" on "public"."donations"; + +drop policy "public can read entries in donations" on "public"."donations"; + +drop policy "public can update entries in donations" on "public"."donations"; + +revoke delete on table "public"."donations" from "anon"; + +revoke insert on table "public"."donations" from "anon"; + +revoke references on table "public"."donations" from "anon"; + +revoke select on table "public"."donations" from "anon"; + +revoke trigger on table "public"."donations" from "anon"; + +revoke truncate on table "public"."donations" from "anon"; + +revoke update on table "public"."donations" from "anon"; + +revoke delete on table "public"."donations" from "authenticated"; + +revoke insert on table "public"."donations" from "authenticated"; + +revoke references on table "public"."donations" from "authenticated"; + +revoke select on table "public"."donations" from "authenticated"; + +revoke trigger on table "public"."donations" from "authenticated"; + +revoke truncate on table "public"."donations" from "authenticated"; + +revoke update on table "public"."donations" from "authenticated"; + +revoke delete on table "public"."donations" from "service_role"; + +revoke insert on table "public"."donations" from "service_role"; + +revoke references on table "public"."donations" from "service_role"; + +revoke select on table "public"."donations" from "service_role"; + +revoke trigger on table "public"."donations" from "service_role"; + +revoke truncate on table "public"."donations" from "service_role"; + +revoke update on table "public"."donations" from "service_role"; + +alter table "public"."donations" drop constraint "donations_pkey"; + +drop index if exists "public"."donations_pkey"; + +drop table "public"."donations"; + + diff --git a/supabase/migrations/20251202064747_fix_donations_type.sql b/supabase/migrations/20251202064747_fix_donations_type.sql new file mode 100644 index 0000000..31012e3 --- /dev/null +++ b/supabase/migrations/20251202064747_fix_donations_type.sql @@ -0,0 +1,106 @@ + + create table "public"."donations" ( + "donation_id" uuid not null default extensions.uuid_generate_v4(), + "receiver_user_id" uuid not null, + "store_id" uuid, + "date_submitted" timestamp with time zone default CURRENT_TIMESTAMP, + "donor_is_individual" boolean not null, + "donor_individual_name" text, + "donor_business_name" text, + "donor_business_contact_name" text, + "donor_email" character varying(255), + "donor_phone" character varying(20), + "donor_street_address" text, + "donor_receive_mailings" boolean not null, + "donor_receive_emails" boolean not null, + "donor_remain_anonymous" boolean not null, + "estimated_value" numeric not null, + "items_donated" text not null + ); + + +alter table "public"."donations" enable row level security; + +CREATE UNIQUE INDEX donations_pkey ON public.donations USING btree (donation_id); + +alter table "public"."donations" add constraint "donations_pkey" PRIMARY KEY using index "donations_pkey"; + +grant delete on table "public"."donations" to "anon"; + +grant insert on table "public"."donations" to "anon"; + +grant references on table "public"."donations" to "anon"; + +grant select on table "public"."donations" to "anon"; + +grant trigger on table "public"."donations" to "anon"; + +grant truncate on table "public"."donations" to "anon"; + +grant update on table "public"."donations" to "anon"; + +grant delete on table "public"."donations" to "authenticated"; + +grant insert on table "public"."donations" to "authenticated"; + +grant references on table "public"."donations" to "authenticated"; + +grant select on table "public"."donations" to "authenticated"; + +grant trigger on table "public"."donations" to "authenticated"; + +grant truncate on table "public"."donations" to "authenticated"; + +grant update on table "public"."donations" to "authenticated"; + +grant delete on table "public"."donations" to "service_role"; + +grant insert on table "public"."donations" to "service_role"; + +grant references on table "public"."donations" to "service_role"; + +grant select on table "public"."donations" to "service_role"; + +grant trigger on table "public"."donations" to "service_role"; + +grant truncate on table "public"."donations" to "service_role"; + +grant update on table "public"."donations" to "service_role"; + + + create policy "public can delete entries in donations" + on "public"."donations" + as permissive + for delete + to anon +using (true); + + + + create policy "public can insert entries in donations" + on "public"."donations" + as permissive + for insert + to anon +with check (true); + + + + create policy "public can read entries in donations" + on "public"."donations" + as permissive + for select + to anon +using (true); + + + + create policy "public can update entries in donations" + on "public"."donations" + as permissive + for update + to anon +with check (true); + + + diff --git a/supabase/schemas/donations.sql b/supabase/schemas/donations.sql index 8f17ec2..8cd726b 100644 --- a/supabase/schemas/donations.sql +++ b/supabase/schemas/donations.sql @@ -9,13 +9,13 @@ create table donations ( receiver_user_id uuid not null, -- references users(id), -- linked to users table, required store_id uuid, -- references stores(id), -- linked to stores table, optional date_submitted timestamp with time zone default current_timestamp, -- generated by default - donor_is_individual boolean not null, -- optional, used if donor is an individual + donor_is_individual boolean not null, -- required, used if donor is an individual donor_individual_name text, -- optional, used if donor is an individual donor_business_name text, -- optional, used if donor is a business donor_business_contact_name text, -- optional, used if donor is a business donor_email varchar(255), -- optional, can't store more than 255 characters donor_phone varchar(20), -- optional, can't store more than 20 characters - donor_street_address boolean not null, -- required, whether or not donor wants to receive mailings + donor_street_address text, -- optional, street address donor_receive_mailings boolean not null, -- required, whether or not donor wants to receive mailings donor_receive_emails boolean not null, -- required, whether or not donor wants to receive emails donor_remain_anonymous boolean not null, -- required, whether or not donor wants to remain anonymous From a30d29e8bc37ec5a19a83d59c43b8e771f3f00f9 Mon Sep 17 00:00:00 2001 From: katieyungchung Date: Mon, 1 Dec 2025 22:57:01 -0800 Subject: [PATCH 09/40] deleted comment --- app/types/Donation.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/types/Donation.ts b/app/types/Donation.ts index 4bea08e..824c447 100644 --- a/app/types/Donation.ts +++ b/app/types/Donation.ts @@ -1,9 +1,3 @@ -/* -TODO: -- Create a Donation type corresponding to the table -- For uuid postgresql data type, use string -*/ - export type DonationType = { donation_id?: string; // optional, generated by database receiver_user_id: string; From a912ccca277870d0485277495ebc4d8c4e265ddb Mon Sep 17 00:00:00 2001 From: 1004yeeun Date: Mon, 1 Dec 2025 23:02:40 -0800 Subject: [PATCH 10/40] Just need to test and add buttons of server actions --- app/(pages)/example/page.tsx | 52 +++++++++++++++++++++++------- app/api/inventory-items/actions.ts | 16 ++++++--- app/types/InventoryItem.ts | 10 +++--- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index bffadf2..80ed25b 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -1,25 +1,55 @@ "use client"; -import { createExampleEntry } from "@/app/api/example/actions"; -import { ExampleType } from "@/app/types/ExampleType"; +//import { createExampleEntry } from "@/app/api/example/actions"; +//import { ExampleType } from "@/app/types/ExampleType"; +import { createItem, changeItemQuantity, deleteItem } from "@/app/api/inventory-items/actions"; +import type { InventoryType } from "@/app/types/InventoryItem"; -export default function Example() { - const data: ExampleType = { - id: 5, - name: 'harry', - }; +// export default function Example() { +// const data: ExampleType = { + +// id: 5, +// name: 'harry', +// }; - const handleExampleClick = async () => { - await createExampleEntry(data); +export default function InventoryType() { + const data: InventoryType = { + inventory_item_id: "1", + store_id: "example", + category: "example", + subcategory: "example", + quantity_available: 2, + item: "Sample Item", + description: "test description", + photo_url: "http://example.com/photo.jpg", + is_hidden: false, }; + // const handleExampleClick = async () => { + // await createExampleEntry(data); + // }; + + const handleCreateItem = async () => { + await createItem(data); + } + + // const changeItemQuantity = async () => { + // await changeItemQuantity(data); + // } + + // const deleteItem = async () => { + // await deleteItem(data); + // } + return (
Welcome to PATH! This is an example page.
- + +
); } \ No newline at end of file diff --git a/app/api/inventory-items/actions.ts b/app/api/inventory-items/actions.ts index f5ee952..87a7ef4 100644 --- a/app/api/inventory-items/actions.ts +++ b/app/api/inventory-items/actions.ts @@ -5,7 +5,7 @@ import { createClient } from "@/app/lib/supabase/server-client"; export const createItem = async (data: InventoryType) => { const supabase = await createClient(); - const { error } = await supabase.from("inventory_items").insert(data); + const { data: entry, error } = await supabase.from("inventory_items").insert(data); if (error) { throw error; @@ -13,9 +13,12 @@ export const createItem = async (data: InventoryType) => { return entry; } -export const changeItemQuantity = async (data: InventoryType) => { +export const changeItemQuantity = async (inventoryId: string, updatedAvailableQuantity: number) => { const supabase = await createClient(); - const { data: entry, error } = await supabase.from("example").insert(data); + const { data: entry, error } = await supabase + .from("inventory_items") + .update({ quantity_available: updatedAvailableQuantity }) + .eq("inventory_item_id", inventoryId); if (error) { throw error; @@ -23,9 +26,12 @@ export const changeItemQuantity = async (data: InventoryType) => { return entry; } -export const deleteItem = async (data: InventoryType) => { +export const deleteItem = async (inventoryId: string) => { const supabase = await createClient(); - const { data: entry, error } = await supabase.from("example").insert(data); + const { data: entry, error } = await supabase + .from("inventory_items") + .delete() + .eq("inventory_item_id", inventoryId); if (error) { throw error; diff --git a/app/types/InventoryItem.ts b/app/types/InventoryItem.ts index 6c30f16..2e976a7 100644 --- a/app/types/InventoryItem.ts +++ b/app/types/InventoryItem.ts @@ -4,12 +4,14 @@ import { UUID } from "crypto" export type InventoryType = { inventory_item_id: string; // UUID - name: Text; + store_id: string; // UUID category: string; subcategory: string; item: string; - description: Text; - photo_url: Text; + description: string; + photo_url: string; quantity_available: number; is_hidden: boolean; -}; \ No newline at end of file +}; + +export type InventoryItemUpdate = Partial; \ No newline at end of file From 80a12a23ca2c5b8e88b98f69065b144bc178fd7a Mon Sep 17 00:00:00 2001 From: Raymond Kao Date: Mon, 1 Dec 2025 23:04:46 -0800 Subject: [PATCH 11/40] ws 5 Co-authored-by: Neelesh Seerapu --- app/(pages)/example/page.tsx | 15 +++ app/api/tickets/actions.ts | 31 +++++- app/types/{Ticket.ts => Ticket.d.ts} | 0 .../20251127034402_create_tickets_table.sql | 96 +++++++++++++++++++ 4 files changed, 139 insertions(+), 3 deletions(-) rename app/types/{Ticket.ts => Ticket.d.ts} (100%) create mode 100644 supabase/migrations/20251127034402_create_tickets_table.sql diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index 5a80c05..01d6825 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -1,6 +1,7 @@ "use client"; import { createExampleEntry } from "@/app/api/example/actions"; +import { createTicket } from "@/app/api/tickets/actions" export default function Example() { const data = { @@ -8,10 +9,20 @@ export default function Example() { name: 'harry', }; + const ticketData = { + requestor_user_id: "4c4f3502-1b31-4040-8a7a-2baedbc8a347", + store_id: "94b6329f-7383-46e2-978d-e105d31c3813", + status: "Pending", + } + const handleClick = async () => { await createExampleEntry(data); }; + const submitTicket = async () => { + await createTicket(ticketData); + } + return (
Welcome to PATH! This is an example page. @@ -19,6 +30,10 @@ export default function Example() { +
+
); } \ No newline at end of file diff --git a/app/api/tickets/actions.ts b/app/api/tickets/actions.ts index 09ac900..a0ed3aa 100644 --- a/app/api/tickets/actions.ts +++ b/app/api/tickets/actions.ts @@ -1,9 +1,34 @@ "use server"; // directive that indicates that the functions we define are server actions +import { Ticket } from "@/app/types/Ticket"; import { createClient } from "@/app/lib/supabase/server-client"; -/*export async function createTicket(formData) { +export async function createTicket(formData: Ticket) { const supabase = await createClient(); // pulling out the error from the insert into tickets table - const { error: err } = await supabase.from('tickets').insert({ticket_id: }) -}*/ + const { error: err } = await supabase.from("tickets").insert({ + requestor_user_id: formData.requestor_user_id, + store_id: formData.store_id, + status: formData.status, + }); + + if (err) { + return { success: false, error: err }; + } + return { success: true }; +} + +export async function deleteTickets(ticketId: string) { + const supabase = await createClient(); + const { error: err } = await supabase.from("tickets").delete().eq("ticket_id", ticketId); + if (err) { + return { success: false, error: err }; + } + return { success: true }; +} + +export async function updateTicketStatus(status: string) { + const supabase = await createClient(); + const { error: err } = await supabase.from("tickets") + //finish and check doc for the 4 status can also make enum +} diff --git a/app/types/Ticket.ts b/app/types/Ticket.d.ts similarity index 100% rename from app/types/Ticket.ts rename to app/types/Ticket.d.ts diff --git a/supabase/migrations/20251127034402_create_tickets_table.sql b/supabase/migrations/20251127034402_create_tickets_table.sql new file mode 100644 index 0000000..da37c56 --- /dev/null +++ b/supabase/migrations/20251127034402_create_tickets_table.sql @@ -0,0 +1,96 @@ + + create table "public"."tickets" ( + "ticket_id" uuid not null default gen_random_uuid(), + "requestor_user_id" uuid, + "store_id" uuid, + "status" character varying(50), + "date_submitted" timestamp with time zone default now() + ); + + +alter table "public"."tickets" enable row level security; + +CREATE UNIQUE INDEX tickets_pkey ON public.tickets USING btree (ticket_id); + +alter table "public"."tickets" add constraint "tickets_pkey" PRIMARY KEY using index "tickets_pkey"; + +grant delete on table "public"."tickets" to "anon"; + +grant insert on table "public"."tickets" to "anon"; + +grant references on table "public"."tickets" to "anon"; + +grant select on table "public"."tickets" to "anon"; + +grant trigger on table "public"."tickets" to "anon"; + +grant truncate on table "public"."tickets" to "anon"; + +grant update on table "public"."tickets" to "anon"; + +grant delete on table "public"."tickets" to "authenticated"; + +grant insert on table "public"."tickets" to "authenticated"; + +grant references on table "public"."tickets" to "authenticated"; + +grant select on table "public"."tickets" to "authenticated"; + +grant trigger on table "public"."tickets" to "authenticated"; + +grant truncate on table "public"."tickets" to "authenticated"; + +grant update on table "public"."tickets" to "authenticated"; + +grant delete on table "public"."tickets" to "service_role"; + +grant insert on table "public"."tickets" to "service_role"; + +grant references on table "public"."tickets" to "service_role"; + +grant select on table "public"."tickets" to "service_role"; + +grant trigger on table "public"."tickets" to "service_role"; + +grant truncate on table "public"."tickets" to "service_role"; + +grant update on table "public"."tickets" to "service_role"; + + + create policy "public can delete entries in tickets" + on "public"."tickets" + as permissive + for delete + to anon +using (true); + + + + create policy "public can insert entries in tickets" + on "public"."tickets" + as permissive + for insert + to anon +with check (true); + + + + create policy "public can read entries in tickets" + on "public"."tickets" + as permissive + for select + to anon +using (true); + + + + create policy "public can update entries in tickets" + on "public"."tickets" + as permissive + for update + to anon +using (true) +with check (true); + + + From 21949b97d15e772ac589eb34651234fb418caf2c Mon Sep 17 00:00:00 2001 From: cheyennelu17 Date: Tue, 2 Dec 2025 17:11:59 -0800 Subject: [PATCH 12/40] ticket item db setup and actions --- app/api/ticket-items/action.ts | 63 +++++++++++++ app/types/Ticket.ts | 10 ++ .../20251201081622_create_ticket_items.sql | 93 +++++++++++++++++++ supabase/schemas/ticket_items.sql | 39 ++++++++ 4 files changed, 205 insertions(+) create mode 100644 app/api/ticket-items/action.ts create mode 100644 app/types/Ticket.ts create mode 100644 supabase/migrations/20251201081622_create_ticket_items.sql create mode 100644 supabase/schemas/ticket_items.sql diff --git a/app/api/ticket-items/action.ts b/app/api/ticket-items/action.ts new file mode 100644 index 0000000..fd0c4f4 --- /dev/null +++ b/app/api/ticket-items/action.ts @@ -0,0 +1,63 @@ +"use server"; + +import { createClient } from "@/app/lib/supabase/server-client"; + +export async function createTicketItem(ticketItemData) { + const supabase = await createClient(); + // Server Action uses a mutation method + const { error } = await supabase.from("ticket_items").insert({ + // ticket_item_id: removed since db auto generates + ticket_id: ticketItemData.get("ticket_id"), + inventory_item_id: ticketItemData.get("inventory_item_id") ?? null, + free_text_description: ticketItemData.get("free_text_description") ?? null, + quantity_requested: ticketItemData.get("quantity_requested") ?? null, + is_in_stock_request: ticketItemData.get("is_in_stock_request"), + }); + //.select().single() <- kept only for debugging to see inserted entry + if (error) { + console.error("Error inserting ticket item:", error); + throw new Error(error.message); + } +} + +export async function changeTicketItemQuantity( + ticketItemId: string, + newQuantity: number | null +) { + const supabase = await createClient(); + const { error } = await supabase + .from("ticket_items") + .update({ quantity_requested: newQuantity }) + .eq("ticket_item_id", ticketItemId); + if (error) { + console.log("Error updating ticket item:", error); + throw new Error(error.message); + } +} + +export async function changeTicketItemDescription( + ticket_item_id: string, + newDescription: string +) { + const supabase = await createClient(); + const { error } = await supabase + .from("ticket_items") + .update({ free_text_description: newDescription }) + .eq("ticket_item_id", ticket_item_id); + if (error) { + console.log("Error updating ticket free text description:", error); + throw new Error(error.message); + } +} + +export async function deleteTicketItem(ticket_item_id: string) { + const supabase = await createClient(); + const { error } = await supabase + .from("ticket_items") + .delete() + .eq("ticket_item_id", ticket_item_id); + if (error) { + console.log("Error deleting ticket:", error); + throw new Error(error.message); + } +} diff --git a/app/types/Ticket.ts b/app/types/Ticket.ts new file mode 100644 index 0000000..e6fe159 --- /dev/null +++ b/app/types/Ticket.ts @@ -0,0 +1,10 @@ +export type TicketItem = { + ticket_item_id: string; + ticket_id: string; + inventory_item_id?: string | null; + free_text_description?: string | null; + quantity_requested?: number | null; + is_in_stock_request: boolean; +}; + +export type TicketItemUpdate = Partial; diff --git a/supabase/migrations/20251201081622_create_ticket_items.sql b/supabase/migrations/20251201081622_create_ticket_items.sql new file mode 100644 index 0000000..16fed36 --- /dev/null +++ b/supabase/migrations/20251201081622_create_ticket_items.sql @@ -0,0 +1,93 @@ + + create table "public"."ticket_items" ( + "ticket_item_id" uuid default extensions.uuid_generate_v4(), + "ticket_id" uuid not null, + "inventory_item_id" uuid, + "free_text_description" text, + "quantity_requested" integer, + "is_in_stock_request" boolean not null + ); + + +alter table "public"."ticket_items" enable row level security; + +grant delete on table "public"."ticket_items" to "anon"; + +grant insert on table "public"."ticket_items" to "anon"; + +grant references on table "public"."ticket_items" to "anon"; + +grant select on table "public"."ticket_items" to "anon"; + +grant trigger on table "public"."ticket_items" to "anon"; + +grant truncate on table "public"."ticket_items" to "anon"; + +grant update on table "public"."ticket_items" to "anon"; + +grant delete on table "public"."ticket_items" to "authenticated"; + +grant insert on table "public"."ticket_items" to "authenticated"; + +grant references on table "public"."ticket_items" to "authenticated"; + +grant select on table "public"."ticket_items" to "authenticated"; + +grant trigger on table "public"."ticket_items" to "authenticated"; + +grant truncate on table "public"."ticket_items" to "authenticated"; + +grant update on table "public"."ticket_items" to "authenticated"; + +grant delete on table "public"."ticket_items" to "service_role"; + +grant insert on table "public"."ticket_items" to "service_role"; + +grant references on table "public"."ticket_items" to "service_role"; + +grant select on table "public"."ticket_items" to "service_role"; + +grant trigger on table "public"."ticket_items" to "service_role"; + +grant truncate on table "public"."ticket_items" to "service_role"; + +grant update on table "public"."ticket_items" to "service_role"; + + + create policy "public can delete tiecket_items" + on "public"."ticket_items" + as permissive + for delete + to anon +using (true); + + + + create policy "public can insert ticket_items" + on "public"."ticket_items" + as permissive + for insert + to anon +with check (true); + + + + create policy "public can read ticket_items" + on "public"."ticket_items" + as permissive + for select + to anon +using (true); + + + + create policy "public can update ticket_items" + on "public"."ticket_items" + as permissive + for update + to anon +using (true) +with check (true); + + + diff --git a/supabase/schemas/ticket_items.sql b/supabase/schemas/ticket_items.sql new file mode 100644 index 0000000..3e1e8ee --- /dev/null +++ b/supabase/schemas/ticket_items.sql @@ -0,0 +1,39 @@ +CREATE TABLE ticket_items +( + + ticket_item_id UUID default uuid_generate_v4() /*primary key*/, + ticket_id UUID not null, + inventory_item_id UUID, + free_text_description TEXT, + quantity_requested INT, + is_in_stock_request BOOLEAN not null + + /* CONSTRAINT fk_tickets + FOREIGN KEY ticket_id + REFERENCES tickets(ticket_id) + CONSTRAINT fk_inventory_items + FOREIGN KEY inventory_item_id + REFERENCES inventory_items(inventory_item_id)*/ +); +alter table "ticket_items" enable row level security; + +create policy "public can read ticket_items" +on public.ticket_items +for select to anon +using (true); + +create policy "public can insert ticket_items" +on public.ticket_items +for insert to anon +with check (true); + +create policy "public can update ticket_items" +on public.ticket_items +for update to anon +using (true) +with check (true); + +create policy "public can delete tiecket_items" +on public.ticket_items +for delete to anon +using (true); \ No newline at end of file From c080feac775cb01ea73f7814d2c8b610b9025a19 Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Thu, 4 Dec 2025 19:48:37 -0800 Subject: [PATCH 13/40] Define DonationInsert type --- app/api/donations/actions.ts | 9 +++------ app/types/Donation.ts | 6 ++++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/api/donations/actions.ts b/app/api/donations/actions.ts index 24ba958..b73f018 100644 --- a/app/api/donations/actions.ts +++ b/app/api/donations/actions.ts @@ -1,17 +1,14 @@ "use server"; -import { DonationType } from '@/app/types/Donation'; +import { DonationInsert } from '@/app/types/Donation'; import { createClient } from '@/app/lib/supabase/server-client'; -export async function createDonation(data: DonationType) { +export async function createDonation(data: DonationInsert) { const supabase = await createClient(); - // remove fields that are auto-generated by database - const { donation_id, date_submitted, ...cleanedData } = data; - const { data: inserted, error } = await supabase .from("donations") - .insert(cleanedData) + .insert(data) .select() // after inserting, return full new row .single(); // expect just one row, if more or less, throw an error diff --git a/app/types/Donation.ts b/app/types/Donation.ts index 824c447..bd7e782 100644 --- a/app/types/Donation.ts +++ b/app/types/Donation.ts @@ -1,4 +1,4 @@ -export type DonationType = { +export type Donation = { donation_id?: string; // optional, generated by database receiver_user_id: string; store_id?: string | null; @@ -20,4 +20,6 @@ export type DonationType = { estimated_value: number; items_donated: string; -}; \ No newline at end of file +}; + +export type DonationInsert = Omit \ No newline at end of file From 21e93369e3adafe98df69767bf37357671866317 Mon Sep 17 00:00:00 2001 From: minado05 Date: Thu, 4 Dec 2025 21:14:06 -0800 Subject: [PATCH 14/40] updated store setup --- app/(pages)/example/page.tsx | 20 ++++++++++--------- app/api/example/stores/action.ts | 8 ++++---- .../20251205044154_create_example_table.sql | 11 ++++++++++ supabase/schemas/stores.sql | 2 +- 4 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 supabase/migrations/20251205044154_create_example_table.sql diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index 4a7e8f0..7fef2e6 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -13,7 +13,7 @@ export default function Example() { }; const storeData: Store = { - store_id: "123", + store_id: "550e8400-e29b-41d4-a716-446655440000", name: "test store", street_address: "test address", }; @@ -27,13 +27,15 @@ export default function Example() { }; const handleDeleteStoreClick = async () => { - await deleteStore("123"); + await deleteStore(storeData.store_id); }; -
- Welcome to PATH! This is an example page. -
- - - -
; + return ( +
+ Welcome to PATH! This is an example page. +
+ + + +
+ ); } diff --git a/app/api/example/stores/action.ts b/app/api/example/stores/action.ts index 04b19b4..cadb900 100644 --- a/app/api/example/stores/action.ts +++ b/app/api/example/stores/action.ts @@ -16,12 +16,12 @@ export const createStore = async (data: Store) => { // delete store given store_id export const deleteStore = async (store_id: string) => { const supabase = await createClient(); - const { data: deletedData, error } = await supabase.from("stores").delete().eq(store_id, 1); - + const { data: deletedData, error } = await supabase + .from("stores") + .delete() + .eq("store_id", "550e8400-e29b-41d4-a716-446655440000"); if (error) { throw error; } return deletedData; }; - - diff --git a/supabase/migrations/20251205044154_create_example_table.sql b/supabase/migrations/20251205044154_create_example_table.sql new file mode 100644 index 0000000..03082f4 --- /dev/null +++ b/supabase/migrations/20251205044154_create_example_table.sql @@ -0,0 +1,11 @@ +alter table "public"."stores" alter column "store_id" set default extensions.uuid_generate_v4(); + +alter table "public"."stores" alter column "store_id" set not null; + +alter table "public"."stores" alter column "store_id" set data type uuid using "store_id"::uuid; + +CREATE UNIQUE INDEX stores_pkey ON public.stores USING btree (store_id); + +alter table "public"."stores" add constraint "stores_pkey" PRIMARY KEY using index "stores_pkey"; + + diff --git a/supabase/schemas/stores.sql b/supabase/schemas/stores.sql index 80db824..c30022e 100644 --- a/supabase/schemas/stores.sql +++ b/supabase/schemas/stores.sql @@ -1,5 +1,5 @@ create table "stores" ( - store_id text, + store_id uuid default uuid_generate_v4() primary key, name text, street_address text ); From c603eec185185c8fa16e8edea01e328facd50165 Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Thu, 4 Dec 2025 21:15:05 -0800 Subject: [PATCH 15/40] Refactor testing --- app/(pages)/example/page.tsx | 2 ++ app/components/test/donations.tsx | 49 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 app/components/test/donations.tsx diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index bffadf2..9cbe716 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -2,6 +2,7 @@ import { createExampleEntry } from "@/app/api/example/actions"; import { ExampleType } from "@/app/types/ExampleType"; +import TestDonationsPage from "../test-donations/page"; export default function Example() { const data: ExampleType = { @@ -20,6 +21,7 @@ export default function Example() { + ); } \ No newline at end of file diff --git a/app/components/test/donations.tsx b/app/components/test/donations.tsx new file mode 100644 index 0000000..3d78fc2 --- /dev/null +++ b/app/components/test/donations.tsx @@ -0,0 +1,49 @@ +"use client"; + +import { createDonation, deleteDonation } from "@/app/api/donations/actions"; + +export default function TestDonationsPage() { + + const handleCreate = async () => { + try { + const donation = await createDonation({ + receiver_user_id: "00000000-0000-0000-0000-000000000000", + store_id: null, + donor_is_individual: true, + donor_individual_name: "Test User", + donor_business_name: "Donor Business Name", + donor_business_contact_name: "Ms. Donor", + donor_email: "test@example.com", + donor_phone: "123-456-7890", + donor_street_address: "1234 Bruin Ave", + donor_receive_mailings: false, + donor_receive_emails: true, + donor_remain_anonymous: false, + estimated_value: 50, + items_donated: "Items donated!" + }); + + console.log("Created donation:", donation); + } catch (err) { + console.error("Create failed:", err); + } + }; + + const handleDelete = async () => { + const idToDelete = "5001e28d-91b6-4df8-8976-4c88c4ad112f"; // hard-coded ID for now + try { + const result = await deleteDonation(idToDelete); + console.log("Deleted donation:", result); + } catch (err) { + console.error("Delete failed:", err); + } + }; + + return ( +
+

Test Donations

+ + +
+ ); +} From b23f0bdbab7cc791d7043c642f19faaa7abcbfba Mon Sep 17 00:00:00 2001 From: minado05 Date: Thu, 4 Dec 2025 21:15:12 -0800 Subject: [PATCH 16/40] updated store setup --- ...e_example_table.sql => 20251205044154_update_stores_table.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename supabase/migrations/{20251205044154_create_example_table.sql => 20251205044154_update_stores_table.sql} (100%) diff --git a/supabase/migrations/20251205044154_create_example_table.sql b/supabase/migrations/20251205044154_update_stores_table.sql similarity index 100% rename from supabase/migrations/20251205044154_create_example_table.sql rename to supabase/migrations/20251205044154_update_stores_table.sql From 0c12c3147d9d174954e96d737c4920e762d95f0c Mon Sep 17 00:00:00 2001 From: Camillalalala Date: Thu, 4 Dec 2025 21:23:28 -0800 Subject: [PATCH 17/40] Successfully implemented createItem() button --- app/(pages)/example/page.tsx | 5 ++--- app/types/InventoryItem.ts | 1 - ...ry_items_table.sql => 20251205050748_inventory_items.sql} | 0 3 files changed, 2 insertions(+), 4 deletions(-) rename supabase/migrations/{20251125063858_create_inventory_items_table.sql => 20251205050748_inventory_items.sql} (100%) diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index 80ed25b..2badb54 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -14,14 +14,13 @@ import type { InventoryType } from "@/app/types/InventoryItem"; export default function InventoryType() { const data: InventoryType = { - inventory_item_id: "1", - store_id: "example", + store_id: "4c7b2b65-16df-44b5-a3c2-e2fcd090b76c", category: "example", subcategory: "example", - quantity_available: 2, item: "Sample Item", description: "test description", photo_url: "http://example.com/photo.jpg", + quantity_available: 2, is_hidden: false, }; diff --git a/app/types/InventoryItem.ts b/app/types/InventoryItem.ts index 2e976a7..eb88083 100644 --- a/app/types/InventoryItem.ts +++ b/app/types/InventoryItem.ts @@ -3,7 +3,6 @@ import { UUID } from "crypto" export type InventoryType = { - inventory_item_id: string; // UUID store_id: string; // UUID category: string; subcategory: string; diff --git a/supabase/migrations/20251125063858_create_inventory_items_table.sql b/supabase/migrations/20251205050748_inventory_items.sql similarity index 100% rename from supabase/migrations/20251125063858_create_inventory_items_table.sql rename to supabase/migrations/20251205050748_inventory_items.sql From 1b32597427f0d2a29e85b6e3d2134e8a964b2423 Mon Sep 17 00:00:00 2001 From: Camillalalala Date: Thu, 4 Dec 2025 21:42:44 -0800 Subject: [PATCH 18/40] Successfully implemented button to update inventory item quantity --- app/(pages)/example/page.tsx | 41 ++++++++++++++++++++++++++++++------ app/types/InventoryItem.ts | 1 + 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index 2badb54..e429408 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -1,8 +1,10 @@ "use client"; +import { useState } from "react"; + //import { createExampleEntry } from "@/app/api/example/actions"; //import { ExampleType } from "@/app/types/ExampleType"; -import { createItem, changeItemQuantity, deleteItem } from "@/app/api/inventory-items/actions"; +import { createItem, changeItemQuantity } from "@/app/api/inventory-items/actions"; import type { InventoryType } from "@/app/types/InventoryItem"; // export default function Example() { @@ -13,6 +15,9 @@ import type { InventoryType } from "@/app/types/InventoryItem"; // }; export default function InventoryType() { + const [inventoryId, setInventoryId] = useState(""); + const [updatedQuantity, setUpdatedQuantity] = useState(0); + const data: InventoryType = { store_id: "4c7b2b65-16df-44b5-a3c2-e2fcd090b76c", category: "example", @@ -32,9 +37,10 @@ export default function InventoryType() { await createItem(data); } - // const changeItemQuantity = async () => { - // await changeItemQuantity(data); - // } + const handleChangeItemQuantity = async () => { + if (!inventoryId) return; + await changeItemQuantity(inventoryId, Number(updatedQuantity)); + } // const deleteItem = async () => { // await deleteItem(data); @@ -47,8 +53,31 @@ export default function InventoryType() { - - +
+ +
+ +
+ ); } \ No newline at end of file diff --git a/app/types/InventoryItem.ts b/app/types/InventoryItem.ts index eb88083..8dfaea3 100644 --- a/app/types/InventoryItem.ts +++ b/app/types/InventoryItem.ts @@ -3,6 +3,7 @@ import { UUID } from "crypto" export type InventoryType = { + inventory_item_id?: string; // UUID store_id: string; // UUID category: string; subcategory: string; From ade046a24425a83c2370941ad1fde4c82de1e0de Mon Sep 17 00:00:00 2001 From: Camillalalala Date: Thu, 4 Dec 2025 21:54:37 -0800 Subject: [PATCH 19/40] Successfully implemented button to delete inventory item --- app/(pages)/example/page.tsx | 67 +++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index e429408..85c5918 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -4,7 +4,7 @@ import { useState } from "react"; //import { createExampleEntry } from "@/app/api/example/actions"; //import { ExampleType } from "@/app/types/ExampleType"; -import { createItem, changeItemQuantity } from "@/app/api/inventory-items/actions"; +import { createItem, changeItemQuantity, deleteItem} from "@/app/api/inventory-items/actions"; import type { InventoryType } from "@/app/types/InventoryItem"; // export default function Example() { @@ -16,6 +16,7 @@ import type { InventoryType } from "@/app/types/InventoryItem"; export default function InventoryType() { const [inventoryId, setInventoryId] = useState(""); + const [inventoryIdToDelete, setInventoryIdToDelete] = useState(""); const [updatedQuantity, setUpdatedQuantity] = useState(0); const data: InventoryType = { @@ -38,46 +39,70 @@ export default function InventoryType() { } const handleChangeItemQuantity = async () => { - if (!inventoryId) return; await changeItemQuantity(inventoryId, Number(updatedQuantity)); } - // const deleteItem = async () => { - // await deleteItem(data); - // } + const handleDeleteItem = async () => { + await deleteItem(inventoryIdToDelete); + } return (
Welcome to PATH! This is an example page.
+
+

Create an Item


-
); } \ No newline at end of file From fd0b81e65f06250286547cc44d4d58ffd559d75c Mon Sep 17 00:00:00 2001 From: Camillalalala Date: Thu, 4 Dec 2025 22:03:32 -0800 Subject: [PATCH 20/40] Updated migration file to remove category field and update subcategory type --- app/(pages)/example/page.tsx | 3 +-- app/types/InventoryItem.ts | 3 +-- ..._inventory_items.sql => 20251205055906_inventory_items.sql} | 3 +-- supabase/schemas/inventory_items.sql | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) rename supabase/migrations/{20251205050748_inventory_items.sql => 20251205055906_inventory_items.sql} (96%) diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index 85c5918..16b4455 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -21,8 +21,7 @@ export default function InventoryType() { const data: InventoryType = { store_id: "4c7b2b65-16df-44b5-a3c2-e2fcd090b76c", - category: "example", - subcategory: "example", + subcategory: 1, item: "Sample Item", description: "test description", photo_url: "http://example.com/photo.jpg", diff --git a/app/types/InventoryItem.ts b/app/types/InventoryItem.ts index 8dfaea3..53f7391 100644 --- a/app/types/InventoryItem.ts +++ b/app/types/InventoryItem.ts @@ -5,8 +5,7 @@ import { UUID } from "crypto" export type InventoryType = { inventory_item_id?: string; // UUID store_id: string; // UUID - category: string; - subcategory: string; + subcategory: number; item: string; description: string; photo_url: string; diff --git a/supabase/migrations/20251205050748_inventory_items.sql b/supabase/migrations/20251205055906_inventory_items.sql similarity index 96% rename from supabase/migrations/20251205050748_inventory_items.sql rename to supabase/migrations/20251205055906_inventory_items.sql index 1573cb1..ec2e284 100644 --- a/supabase/migrations/20251205050748_inventory_items.sql +++ b/supabase/migrations/20251205055906_inventory_items.sql @@ -2,8 +2,7 @@ create table "public"."inventory_items" ( "inventory_item_id" uuid not null default extensions.uuid_generate_v4(), "store_id" uuid not null, - "category" character varying(50) not null, - "subcategory" character varying(50) not null, + "subcategory" integer not null, "item" character varying(255) not null, "description" text not null, "photo_url" text, diff --git a/supabase/schemas/inventory_items.sql b/supabase/schemas/inventory_items.sql index c36ed61..8b1fee1 100644 --- a/supabase/schemas/inventory_items.sql +++ b/supabase/schemas/inventory_items.sql @@ -1,8 +1,7 @@ create table "inventory_items" ( "inventory_item_id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(), "store_id" UUID not null, - "category" varchar(50) not null, - "subcategory" varchar(50) not null, + "subcategory" int not null, "item" varchar(255) not null, "description" text not null, "photo_url" text, From a7d1f32f3a1e4f0bd1b3fbedb26c0fb8452bf52e Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Thu, 4 Dec 2025 22:05:17 -0800 Subject: [PATCH 21/40] Add primary key, minor fixes --- app/api/ticket-items/action.ts | 1 + app/types/Ticket.ts | 2 ++ .../migrations/20251205055058_add_primary_key.sql | 7 +++++++ supabase/migrations/20251205060123_fix_typo.sql | 12 ++++++++++++ supabase/schemas/ticket_items.sql | 5 ++--- 5 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 supabase/migrations/20251205055058_add_primary_key.sql create mode 100644 supabase/migrations/20251205060123_fix_typo.sql diff --git a/app/api/ticket-items/action.ts b/app/api/ticket-items/action.ts index fd0c4f4..911d4c0 100644 --- a/app/api/ticket-items/action.ts +++ b/app/api/ticket-items/action.ts @@ -1,5 +1,6 @@ "use server"; +import { TicketItemInsert } from '@/app/types/Ticket'; import { createClient } from "@/app/lib/supabase/server-client"; export async function createTicketItem(ticketItemData) { diff --git a/app/types/Ticket.ts b/app/types/Ticket.ts index e6fe159..ef8b1c5 100644 --- a/app/types/Ticket.ts +++ b/app/types/Ticket.ts @@ -8,3 +8,5 @@ export type TicketItem = { }; export type TicketItemUpdate = Partial; + +export type TicketItemInsert = Omit diff --git a/supabase/migrations/20251205055058_add_primary_key.sql b/supabase/migrations/20251205055058_add_primary_key.sql new file mode 100644 index 0000000..0dfa520 --- /dev/null +++ b/supabase/migrations/20251205055058_add_primary_key.sql @@ -0,0 +1,7 @@ +alter table "public"."ticket_items" alter column "ticket_item_id" set not null; + +CREATE UNIQUE INDEX ticket_items_pkey ON public.ticket_items USING btree (ticket_item_id); + +alter table "public"."ticket_items" add constraint "ticket_items_pkey" PRIMARY KEY using index "ticket_items_pkey"; + + diff --git a/supabase/migrations/20251205060123_fix_typo.sql b/supabase/migrations/20251205060123_fix_typo.sql new file mode 100644 index 0000000..2e4070f --- /dev/null +++ b/supabase/migrations/20251205060123_fix_typo.sql @@ -0,0 +1,12 @@ +drop policy "public can delete tiecket_items" on "public"."ticket_items"; + + + create policy "public can delete ticket_items" + on "public"."ticket_items" + as permissive + for delete + to anon +using (true); + + + diff --git a/supabase/schemas/ticket_items.sql b/supabase/schemas/ticket_items.sql index 3e1e8ee..93a1171 100644 --- a/supabase/schemas/ticket_items.sql +++ b/supabase/schemas/ticket_items.sql @@ -1,7 +1,6 @@ CREATE TABLE ticket_items ( - - ticket_item_id UUID default uuid_generate_v4() /*primary key*/, + ticket_item_id UUID default uuid_generate_v4() primary key, ticket_id UUID not null, inventory_item_id UUID, free_text_description TEXT, @@ -33,7 +32,7 @@ for update to anon using (true) with check (true); -create policy "public can delete tiecket_items" +create policy "public can delete ticket_items" on public.ticket_items for delete to anon using (true); \ No newline at end of file From 2427d5b120514eaccf4aefce4e528ee19149b1c2 Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Thu, 4 Dec 2025 22:20:55 -0800 Subject: [PATCH 22/40] Move files --- app/(pages)/example/page.tsx | 4 ++-- app/api/{example => }/stores/action.ts | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename app/api/{example => }/stores/action.ts (100%) diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index 4f9d03c..1caa104 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -1,10 +1,10 @@ "use client"; import { createExampleEntry } from "@/app/api/example/actions"; -import { createStore } from "@/app/api/example/stores/action"; +import { createStore } from "@/app/api/stores/action"; import { ExampleType } from "@/app/types/ExampleType"; import { Store } from "@/app/types/Store"; -import { deleteStore } from "@/app/api/example/stores/action"; +import { deleteStore } from "@/app/api/stores/action"; import TestDonationsPage from "../test-donations/page"; export default function Example() { diff --git a/app/api/example/stores/action.ts b/app/api/stores/action.ts similarity index 100% rename from app/api/example/stores/action.ts rename to app/api/stores/action.ts From 295dbcfa57dcc251f2c0a64a7182bf4f3604feae Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Thu, 4 Dec 2025 22:38:33 -0800 Subject: [PATCH 23/40] Add StoreInsert type --- app/api/stores/action.ts | 7 ++++--- app/types/Store.ts | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/api/stores/action.ts b/app/api/stores/action.ts index cadb900..1e4ff33 100644 --- a/app/api/stores/action.ts +++ b/app/api/stores/action.ts @@ -1,9 +1,10 @@ "use server"; + import { createClient } from "@/app/lib/supabase/server-client"; -import { Store } from "@/app/types/Store"; +import { StoreInsert } from "@/app/types/Store"; // create store -export const createStore = async (data: Store) => { +export const createStore = async (data: StoreInsert) => { const supabase = await createClient(); const { data: entry, error } = await supabase.from("stores").insert(data); if (error) { @@ -19,7 +20,7 @@ export const deleteStore = async (store_id: string) => { const { data: deletedData, error } = await supabase .from("stores") .delete() - .eq("store_id", "550e8400-e29b-41d4-a716-446655440000"); + .eq("store_id", store_id); if (error) { throw error; } diff --git a/app/types/Store.ts b/app/types/Store.ts index d81a8c5..a6268b6 100644 --- a/app/types/Store.ts +++ b/app/types/Store.ts @@ -3,3 +3,5 @@ export type Store = { name: string; street_address: string; }; + +export type StoreInsert = Omit \ No newline at end of file From 6af2b000baf5bbaaf3b35d5a326577e172cb48ab Mon Sep 17 00:00:00 2001 From: Raymond Kao Date: Thu, 4 Dec 2025 22:40:37 -0800 Subject: [PATCH 24/40] sprint 1 Co-authored-by: Neelesh Seerapu --- app/(pages)/example/page.tsx | 43 ++++++++- app/api/tickets/actions.ts | 11 ++- .../20251127034402_create_tickets_table.sql | 96 ------------------- ...20251205063407_add_ticket_status_enum.sql} | 4 +- supabase/schemas/tickets.sql | 4 +- supabase/seeds/tickets.sql | 5 - 6 files changed, 54 insertions(+), 109 deletions(-) delete mode 100644 supabase/migrations/20251127034402_create_tickets_table.sql rename supabase/migrations/{20251129181914_create_tickets_table.sql => 20251205063407_add_ticket_status_enum.sql} (94%) delete mode 100644 supabase/seeds/tickets.sql diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index 01d6825..a6ed44f 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -1,18 +1,28 @@ "use client"; import { createExampleEntry } from "@/app/api/example/actions"; -import { createTicket } from "@/app/api/tickets/actions" +import { createTicket, deleteTicket, updateTicketStatus } from "@/app/api/tickets/actions" +import { useState } from "react"; export default function Example() { + + const [ticketToDelete, setDelete] = useState(""); + const [ticketToUpdate, setUpdateTicket] = useState(""); + + const [updateStatus, setStatus] = useState(""); + + const data = { id: 5, name: 'harry', }; const ticketData = { + ticket_id: "", requestor_user_id: "4c4f3502-1b31-4040-8a7a-2baedbc8a347", store_id: "94b6329f-7383-46e2-978d-e105d31c3813", - status: "Pending", + status: "ready", + date_submitted: "" } const handleClick = async () => { @@ -23,6 +33,14 @@ export default function Example() { await createTicket(ticketData); } + const sendTicketDeletion = async () => { + await deleteTicket(ticketToDelete); + } + + const updateTicket = async () => { + await updateTicketStatus(updateStatus, ticketToUpdate); + } + return (
Welcome to PATH! This is an example page. @@ -34,6 +52,27 @@ export default function Example() { +
+ +
+ + +
+ +
+ +
+
); } \ No newline at end of file diff --git a/app/api/tickets/actions.ts b/app/api/tickets/actions.ts index a0ed3aa..b6a4bd5 100644 --- a/app/api/tickets/actions.ts +++ b/app/api/tickets/actions.ts @@ -18,7 +18,7 @@ export async function createTicket(formData: Ticket) { return { success: true }; } -export async function deleteTickets(ticketId: string) { +export async function deleteTicket(ticketId: string) { const supabase = await createClient(); const { error: err } = await supabase.from("tickets").delete().eq("ticket_id", ticketId); if (err) { @@ -27,8 +27,11 @@ export async function deleteTickets(ticketId: string) { return { success: true }; } -export async function updateTicketStatus(status: string) { +export async function updateTicketStatus(newStatus: string, ticketId: string) { const supabase = await createClient(); - const { error: err } = await supabase.from("tickets") - //finish and check doc for the 4 status can also make enum + const { error: err } = await supabase.from("tickets").update({status: newStatus }).eq("ticket_id", ticketId); + if (err) { + return { success : false, error: err}; + } + return { success : true }; } diff --git a/supabase/migrations/20251127034402_create_tickets_table.sql b/supabase/migrations/20251127034402_create_tickets_table.sql deleted file mode 100644 index da37c56..0000000 --- a/supabase/migrations/20251127034402_create_tickets_table.sql +++ /dev/null @@ -1,96 +0,0 @@ - - create table "public"."tickets" ( - "ticket_id" uuid not null default gen_random_uuid(), - "requestor_user_id" uuid, - "store_id" uuid, - "status" character varying(50), - "date_submitted" timestamp with time zone default now() - ); - - -alter table "public"."tickets" enable row level security; - -CREATE UNIQUE INDEX tickets_pkey ON public.tickets USING btree (ticket_id); - -alter table "public"."tickets" add constraint "tickets_pkey" PRIMARY KEY using index "tickets_pkey"; - -grant delete on table "public"."tickets" to "anon"; - -grant insert on table "public"."tickets" to "anon"; - -grant references on table "public"."tickets" to "anon"; - -grant select on table "public"."tickets" to "anon"; - -grant trigger on table "public"."tickets" to "anon"; - -grant truncate on table "public"."tickets" to "anon"; - -grant update on table "public"."tickets" to "anon"; - -grant delete on table "public"."tickets" to "authenticated"; - -grant insert on table "public"."tickets" to "authenticated"; - -grant references on table "public"."tickets" to "authenticated"; - -grant select on table "public"."tickets" to "authenticated"; - -grant trigger on table "public"."tickets" to "authenticated"; - -grant truncate on table "public"."tickets" to "authenticated"; - -grant update on table "public"."tickets" to "authenticated"; - -grant delete on table "public"."tickets" to "service_role"; - -grant insert on table "public"."tickets" to "service_role"; - -grant references on table "public"."tickets" to "service_role"; - -grant select on table "public"."tickets" to "service_role"; - -grant trigger on table "public"."tickets" to "service_role"; - -grant truncate on table "public"."tickets" to "service_role"; - -grant update on table "public"."tickets" to "service_role"; - - - create policy "public can delete entries in tickets" - on "public"."tickets" - as permissive - for delete - to anon -using (true); - - - - create policy "public can insert entries in tickets" - on "public"."tickets" - as permissive - for insert - to anon -with check (true); - - - - create policy "public can read entries in tickets" - on "public"."tickets" - as permissive - for select - to anon -using (true); - - - - create policy "public can update entries in tickets" - on "public"."tickets" - as permissive - for update - to anon -using (true) -with check (true); - - - diff --git a/supabase/migrations/20251129181914_create_tickets_table.sql b/supabase/migrations/20251205063407_add_ticket_status_enum.sql similarity index 94% rename from supabase/migrations/20251129181914_create_tickets_table.sql rename to supabase/migrations/20251205063407_add_ticket_status_enum.sql index 5a06920..3c4ac0b 100644 --- a/supabase/migrations/20251129181914_create_tickets_table.sql +++ b/supabase/migrations/20251205063407_add_ticket_status_enum.sql @@ -1,9 +1,11 @@ +create type "public"."ticket_status" as enum ('requested', 'ready', 'rejected', 'fulfilled'); + create table "public"."tickets" ( "ticket_id" uuid not null default extensions.uuid_generate_v4(), "requestor_user_id" uuid not null, "store_id" uuid not null, - "status" character varying(50) not null, + "status" public.ticket_status not null, "date_submitted" timestamp with time zone default now() ); diff --git a/supabase/schemas/tickets.sql b/supabase/schemas/tickets.sql index d831b42..4db953e 100644 --- a/supabase/schemas/tickets.sql +++ b/supabase/schemas/tickets.sql @@ -1,8 +1,10 @@ +create type ticket_status as enum ('requested', 'ready', 'rejected', 'fulfilled'); + create table "tickets" ( "ticket_id" uuid default uuid_generate_v4() primary key, "requestor_user_id" uuid not null, "store_id" uuid not null, - "status" VARCHAR(50) not null, -- max 50 character status + "status" ticket_status not null, -- max 50 character status "date_submitted" TIMESTAMP WITH TIME ZONE default now() -- FOREIGN KEY (requestor_user_id) REFERENCES users(user_id), -- FOREIGN KEY (store_id) REFERENCES stores(store_id) diff --git a/supabase/seeds/tickets.sql b/supabase/seeds/tickets.sql deleted file mode 100644 index b739183..0000000 --- a/supabase/seeds/tickets.sql +++ /dev/null @@ -1,5 +0,0 @@ -insert into tickets - (id, name) -values - (1, 'alice'), - (2, 'bob'); \ No newline at end of file From b9b16c04189b19973f978610c9066773f9b3498e Mon Sep 17 00:00:00 2001 From: neelesh Date: Fri, 5 Dec 2025 00:53:19 -0800 Subject: [PATCH 25/40] added the example seed back --- supabase/seeds/example.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 supabase/seeds/example.sql diff --git a/supabase/seeds/example.sql b/supabase/seeds/example.sql new file mode 100644 index 0000000..ddacc6b --- /dev/null +++ b/supabase/seeds/example.sql @@ -0,0 +1,5 @@ +insert into example + (id, name) +values + (1, 'alice'), + (2, 'bob'); From 4775110daaa2abf28f89d5489d6b423f72178266 Mon Sep 17 00:00:00 2001 From: neelesh Date: Fri, 5 Dec 2025 00:58:04 -0800 Subject: [PATCH 26/40] fixed small type checking inconsistency --- app/(pages)/example/page.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index 91d2df4..e7a8ce0 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -9,6 +9,7 @@ import { import { useState } from "react"; import { createStore } from "@/app/api/stores/action"; import { ExampleType } from "@/app/types/ExampleType"; +import { Ticket } from "@/app/types/Ticket"; import { Store } from "@/app/types/Store"; import { deleteStore } from "@/app/api/stores/action"; import TestDonationsPage from "../test-donations/page"; @@ -19,7 +20,7 @@ export default function Example() { const [updateStatus, setStatus] = useState(""); - const data = { + const data: ExampleType = { id: 5, name: "harry", }; @@ -30,7 +31,7 @@ export default function Example() { street_address: "test address", }; - const ticketData = { + const ticketData: Ticket = { ticket_id: "", requestor_user_id: "4c4f3502-1b31-4040-8a7a-2baedbc8a347", store_id: "94b6329f-7383-46e2-978d-e105d31c3813", From 3c12bd5c66c7750ac0d7aa558398443bfbb4895a Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Sun, 7 Dec 2025 18:53:41 -0800 Subject: [PATCH 27/40] Fix Next.js security vulnerabilities --- package-lock.json | 86 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3cef715..91347ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@supabase/ssr": "^0.7.0", "@supabase/supabase-js": "^2.81.1", - "next": "16.0.1", + "next": "^16.0.7", "react": "19.2.0", "react-dom": "19.2.0" }, @@ -1051,9 +1051,9 @@ } }, "node_modules/@next/env": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.1.tgz", - "integrity": "sha512-LFvlK0TG2L3fEOX77OC35KowL8D7DlFF45C0OvKMC4hy8c/md1RC4UMNDlUGJqfCoCS2VWrZ4dSE6OjaX5+8mw==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.7.tgz", + "integrity": "sha512-gpaNgUh5nftFKRkRQGnVi5dpcYSKGcZZkQffZ172OrG/XkrnS7UBTQ648YY+8ME92cC4IojpI2LqTC8sTDhAaw==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -1067,9 +1067,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.1.tgz", - "integrity": "sha512-R0YxRp6/4W7yG1nKbfu41bp3d96a0EalonQXiMe+1H9GTHfKxGNCGFNWUho18avRBPsO8T3RmdWuzmfurlQPbg==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.7.tgz", + "integrity": "sha512-LlDtCYOEj/rfSnEn/Idi+j1QKHxY9BJFmxx7108A6D8K0SB+bNgfYQATPk/4LqOl4C0Wo3LACg2ie6s7xqMpJg==", "cpu": [ "arm64" ], @@ -1083,9 +1083,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.1.tgz", - "integrity": "sha512-kETZBocRux3xITiZtOtVoVvXyQLB7VBxN7L6EPqgI5paZiUlnsgYv4q8diTNYeHmF9EiehydOBo20lTttCbHAg==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.7.tgz", + "integrity": "sha512-rtZ7BhnVvO1ICf3QzfW9H3aPz7GhBrnSIMZyr4Qy6boXF0b5E3QLs+cvJmg3PsTCG2M1PBoC+DANUi4wCOKXpA==", "cpu": [ "x64" ], @@ -1099,9 +1099,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.1.tgz", - "integrity": "sha512-hWg3BtsxQuSKhfe0LunJoqxjO4NEpBmKkE+P2Sroos7yB//OOX3jD5ISP2wv8QdUwtRehMdwYz6VB50mY6hqAg==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.7.tgz", + "integrity": "sha512-mloD5WcPIeIeeZqAIP5c2kdaTa6StwP4/2EGy1mUw8HiexSHGK/jcM7lFuS3u3i2zn+xH9+wXJs6njO7VrAqww==", "cpu": [ "arm64" ], @@ -1115,9 +1115,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.1.tgz", - "integrity": "sha512-UPnOvYg+fjAhP3b1iQStcYPWeBFRLrugEyK/lDKGk7kLNua8t5/DvDbAEFotfV1YfcOY6bru76qN9qnjLoyHCQ==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.7.tgz", + "integrity": "sha512-+ksWNrZrthisXuo9gd1XnjHRowCbMtl/YgMpbRvFeDEqEBd523YHPWpBuDjomod88U8Xliw5DHhekBC3EOOd9g==", "cpu": [ "arm64" ], @@ -1131,9 +1131,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.1.tgz", - "integrity": "sha512-Et81SdWkcRqAJziIgFtsFyJizHoWne4fzJkvjd6V4wEkWTB4MX6J0uByUb0peiJQ4WeAt6GGmMszE5KrXK6WKg==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.7.tgz", + "integrity": "sha512-4WtJU5cRDxpEE44Ana2Xro1284hnyVpBb62lIpU5k85D8xXxatT+rXxBgPkc7C1XwkZMWpK5rXLXTh9PFipWsA==", "cpu": [ "x64" ], @@ -1147,9 +1147,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.1.tgz", - "integrity": "sha512-qBbgYEBRrC1egcG03FZaVfVxrJm8wBl7vr8UFKplnxNRprctdP26xEv9nJ07Ggq4y1adwa0nz2mz83CELY7N6Q==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.7.tgz", + "integrity": "sha512-HYlhqIP6kBPXalW2dbMTSuB4+8fe+j9juyxwfMwCe9kQPPeiyFn7NMjNfoFOfJ2eXkeQsoUGXg+O2SE3m4Qg2w==", "cpu": [ "x64" ], @@ -1163,9 +1163,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.1.tgz", - "integrity": "sha512-cPuBjYP6I699/RdbHJonb3BiRNEDm5CKEBuJ6SD8k3oLam2fDRMKAvmrli4QMDgT2ixyRJ0+DTkiODbIQhRkeQ==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.7.tgz", + "integrity": "sha512-EviG+43iOoBRZg9deGauXExjRphhuYmIOJ12b9sAPy0eQ6iwcPxfED2asb/s2/yiLYOdm37kPaiZu8uXSYPs0Q==", "cpu": [ "arm64" ], @@ -1179,9 +1179,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.1.tgz", - "integrity": "sha512-XeEUJsE4JYtfrXe/LaJn3z1pD19fK0Q6Er8Qoufi+HqvdO4LEPyCxLUt4rxA+4RfYo6S9gMlmzCMU2F+AatFqQ==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.7.tgz", + "integrity": "sha512-gniPjy55zp5Eg0896qSrf3yB1dw4F/3s8VK1ephdsZZ129j2n6e1WqCbE2YgcKhW9hPB9TVZENugquWJD5x0ug==", "cpu": [ "x64" ], @@ -4685,9 +4685,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -5250,12 +5250,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/next/-/next-16.0.1.tgz", - "integrity": "sha512-e9RLSssZwd35p7/vOa+hoDFggUZIUbZhIUSLZuETCwrCVvxOs87NamoUzT+vbcNAL8Ld9GobBnWOA6SbV/arOw==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/next/-/next-16.0.7.tgz", + "integrity": "sha512-3mBRJyPxT4LOxAJI6IsXeFtKfiJUbjCLgvXO02fV8Wy/lIhPvP94Fe7dGhUgHXcQy4sSuYwQNcOLhIfOm0rL0A==", "license": "MIT", "dependencies": { - "@next/env": "16.0.1", + "@next/env": "16.0.7", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -5268,14 +5268,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.0.1", - "@next/swc-darwin-x64": "16.0.1", - "@next/swc-linux-arm64-gnu": "16.0.1", - "@next/swc-linux-arm64-musl": "16.0.1", - "@next/swc-linux-x64-gnu": "16.0.1", - "@next/swc-linux-x64-musl": "16.0.1", - "@next/swc-win32-arm64-msvc": "16.0.1", - "@next/swc-win32-x64-msvc": "16.0.1", + "@next/swc-darwin-arm64": "16.0.7", + "@next/swc-darwin-x64": "16.0.7", + "@next/swc-linux-arm64-gnu": "16.0.7", + "@next/swc-linux-arm64-musl": "16.0.7", + "@next/swc-linux-x64-gnu": "16.0.7", + "@next/swc-linux-x64-musl": "16.0.7", + "@next/swc-win32-arm64-msvc": "16.0.7", + "@next/swc-win32-x64-msvc": "16.0.7", "sharp": "^0.34.4" }, "peerDependencies": { diff --git a/package.json b/package.json index 8b899b9..7e4d8d1 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dependencies": { "@supabase/ssr": "^0.7.0", "@supabase/supabase-js": "^2.81.1", - "next": "16.0.1", + "next": "^16.0.7", "react": "19.2.0", "react-dom": "19.2.0" }, From 3d4424b703df2474d4557b40f87099d082a55c86 Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Sun, 14 Dec 2025 22:33:15 -0800 Subject: [PATCH 28/40] Update Next.js --- package-lock.json | 78 +++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index 91347ea..f94e74a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1051,9 +1051,9 @@ } }, "node_modules/@next/env": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.7.tgz", - "integrity": "sha512-gpaNgUh5nftFKRkRQGnVi5dpcYSKGcZZkQffZ172OrG/XkrnS7UBTQ648YY+8ME92cC4IojpI2LqTC8sTDhAaw==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz", + "integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -1067,9 +1067,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.7.tgz", - "integrity": "sha512-LlDtCYOEj/rfSnEn/Idi+j1QKHxY9BJFmxx7108A6D8K0SB+bNgfYQATPk/4LqOl4C0Wo3LACg2ie6s7xqMpJg==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.10.tgz", + "integrity": "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg==", "cpu": [ "arm64" ], @@ -1083,9 +1083,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.7.tgz", - "integrity": "sha512-rtZ7BhnVvO1ICf3QzfW9H3aPz7GhBrnSIMZyr4Qy6boXF0b5E3QLs+cvJmg3PsTCG2M1PBoC+DANUi4wCOKXpA==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.10.tgz", + "integrity": "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==", "cpu": [ "x64" ], @@ -1099,9 +1099,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.7.tgz", - "integrity": "sha512-mloD5WcPIeIeeZqAIP5c2kdaTa6StwP4/2EGy1mUw8HiexSHGK/jcM7lFuS3u3i2zn+xH9+wXJs6njO7VrAqww==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.10.tgz", + "integrity": "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==", "cpu": [ "arm64" ], @@ -1115,9 +1115,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.7.tgz", - "integrity": "sha512-+ksWNrZrthisXuo9gd1XnjHRowCbMtl/YgMpbRvFeDEqEBd523YHPWpBuDjomod88U8Xliw5DHhekBC3EOOd9g==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.10.tgz", + "integrity": "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==", "cpu": [ "arm64" ], @@ -1131,9 +1131,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.7.tgz", - "integrity": "sha512-4WtJU5cRDxpEE44Ana2Xro1284hnyVpBb62lIpU5k85D8xXxatT+rXxBgPkc7C1XwkZMWpK5rXLXTh9PFipWsA==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.10.tgz", + "integrity": "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==", "cpu": [ "x64" ], @@ -1147,9 +1147,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.7.tgz", - "integrity": "sha512-HYlhqIP6kBPXalW2dbMTSuB4+8fe+j9juyxwfMwCe9kQPPeiyFn7NMjNfoFOfJ2eXkeQsoUGXg+O2SE3m4Qg2w==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.10.tgz", + "integrity": "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==", "cpu": [ "x64" ], @@ -1163,9 +1163,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.7.tgz", - "integrity": "sha512-EviG+43iOoBRZg9deGauXExjRphhuYmIOJ12b9sAPy0eQ6iwcPxfED2asb/s2/yiLYOdm37kPaiZu8uXSYPs0Q==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.10.tgz", + "integrity": "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==", "cpu": [ "arm64" ], @@ -1179,9 +1179,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.7.tgz", - "integrity": "sha512-gniPjy55zp5Eg0896qSrf3yB1dw4F/3s8VK1ephdsZZ129j2n6e1WqCbE2YgcKhW9hPB9TVZENugquWJD5x0ug==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.10.tgz", + "integrity": "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==", "cpu": [ "x64" ], @@ -5250,12 +5250,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/next/-/next-16.0.7.tgz", - "integrity": "sha512-3mBRJyPxT4LOxAJI6IsXeFtKfiJUbjCLgvXO02fV8Wy/lIhPvP94Fe7dGhUgHXcQy4sSuYwQNcOLhIfOm0rL0A==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/next/-/next-16.0.10.tgz", + "integrity": "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA==", "license": "MIT", "dependencies": { - "@next/env": "16.0.7", + "@next/env": "16.0.10", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -5268,14 +5268,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.0.7", - "@next/swc-darwin-x64": "16.0.7", - "@next/swc-linux-arm64-gnu": "16.0.7", - "@next/swc-linux-arm64-musl": "16.0.7", - "@next/swc-linux-x64-gnu": "16.0.7", - "@next/swc-linux-x64-musl": "16.0.7", - "@next/swc-win32-arm64-msvc": "16.0.7", - "@next/swc-win32-x64-msvc": "16.0.7", + "@next/swc-darwin-arm64": "16.0.10", + "@next/swc-darwin-x64": "16.0.10", + "@next/swc-linux-arm64-gnu": "16.0.10", + "@next/swc-linux-arm64-musl": "16.0.10", + "@next/swc-linux-x64-gnu": "16.0.10", + "@next/swc-linux-x64-musl": "16.0.10", + "@next/swc-win32-arm64-msvc": "16.0.10", + "@next/swc-win32-x64-msvc": "16.0.10", "sharp": "^0.34.4" }, "peerDependencies": { From 36fd68e42ee8a60ed2233c2fd906e0f4229c247d Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Sun, 14 Dec 2025 23:37:03 -0800 Subject: [PATCH 29/40] Add Prettier --- .prettierrc.json | 10 ++++++++++ .vscode/settings.json | 4 ++++ eslint.config.mjs | 2 ++ package-lock.json | 34 ++++++++++++++++++++++++++++++++++ package.json | 6 +++++- 5 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 .prettierrc.json create mode 100644 .vscode/settings.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..e731a61 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "semi": true, + "trailingComma": "all", + "singleQuote": true, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "jsxSingleQuote": false, + "arrowParens": "always" +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f82fc39 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true +} \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index 05e726d..9f61ede 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,10 +1,12 @@ import { defineConfig, globalIgnores } from "eslint/config"; import nextVitals from "eslint-config-next/core-web-vitals"; import nextTs from "eslint-config-next/typescript"; +import prettier from "eslint-config-prettier/flat"; const eslintConfig = defineConfig([ ...nextVitals, ...nextTs, + prettier, // Override default ignores of eslint-config-next. globalIgnores([ // Default ignores of eslint-config-next: diff --git a/package-lock.json b/package-lock.json index f94e74a..03e615f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,8 @@ "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "16.0.1", + "eslint-config-prettier": "^10.1.8", + "prettier": "^3.7.4", "supabase": "^2.58.5", "tailwindcss": "^4", "typescript": "^5" @@ -3340,6 +3342,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -5685,6 +5703,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/proc-log": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", diff --git a/package.json b/package.json index 7e4d8d1..357593c 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "eslint" + "lint": "eslint", + "format:check": "prettier --check .", + "format:fix": "prettier --write ." }, "dependencies": { "@supabase/ssr": "^0.7.0", @@ -22,6 +24,8 @@ "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "16.0.1", + "eslint-config-prettier": "^10.1.8", + "prettier": "^3.7.4", "supabase": "^2.58.5", "tailwindcss": "^4", "typescript": "^5" From 4fcdf499d6699574a96d6ec6e6248f5d27b95936 Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Mon, 15 Dec 2025 00:05:50 -0800 Subject: [PATCH 30/40] Add Prettier for SQL --- .prettierignore | 1 + .prettierrc.json | 15 ++++- package-lock.json | 153 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 4 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..6c6067f --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +supabase/migrations \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json index e731a61..93c54d9 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -6,5 +6,16 @@ "tabWidth": 2, "useTabs": false, "jsxSingleQuote": false, - "arrowParens": "always" -} \ No newline at end of file + "arrowParens": "always", + "plugins": ["prettier-plugin-sql"], + "overrides": [ + { + "files": "*.sql", + "options": { + "language": "postgresql", + "keywordCase": "lower", + "dataTypeCase": "lower" + } + } + ] +} diff --git a/package-lock.json b/package-lock.json index 03e615f..54b511a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "eslint-config-next": "16.0.1", "eslint-config-prettier": "^10.1.8", "prettier": "^3.7.4", + "prettier-plugin-sql": "^0.19.2", "supabase": "^2.58.5", "tailwindcss": "^4", "typescript": "^5" @@ -1663,6 +1664,13 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/pegjs": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/@types/pegjs/-/pegjs-0.10.6.tgz", + "integrity": "sha512-eLYXDbZWXh2uxf+w8sXS8d6KSoXTswfps6fvCUuVAGN8eRpfe7h9eSRydxiSJvo9Bf+GzifsDOr9TMQlmJdmkw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/phoenix": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", @@ -2581,6 +2589,16 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, "node_modules/bin-links": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-6.0.0.tgz", @@ -2799,6 +2817,13 @@ "dev": true, "license": "MIT" }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2986,6 +3011,13 @@ "node": ">=8" } }, + "node_modules/discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", + "dev": true, + "license": "MIT" + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -4762,6 +4794,16 @@ "node": ">=6" } }, + "node_modules/jsox": { + "version": "1.2.124", + "resolved": "https://registry.npmjs.org/jsox/-/jsox-1.2.124.tgz", + "integrity": "sha512-KwptI8xUfOt7qJ+P+2utrJIEHTgDjXpBsDhPSM4GDq++4by+LX9nUFee97QfRloGcMh5iA9Ha/4UCVZl7zWzuA==", + "dev": true, + "license": "MIT", + "bin": { + "jsox": "lib/cli.js" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -5219,6 +5261,13 @@ "node": ">= 18" } }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5267,6 +5316,29 @@ "dev": true, "license": "MIT" }, + "node_modules/nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "bin": { + "nearley-railroad": "bin/nearley-railroad.js", + "nearley-test": "bin/nearley-test.js", + "nearley-unparse": "bin/nearley-unparse.js", + "nearleyc": "bin/nearleyc.js" + }, + "funding": { + "type": "individual", + "url": "https://nearley.js.org/#give-to-nearley" + } + }, "node_modules/next": { "version": "16.0.10", "resolved": "https://registry.npmjs.org/next/-/next-16.0.10.tgz", @@ -5394,6 +5466,20 @@ "dev": true, "license": "MIT" }, + "node_modules/node-sql-parser": { + "version": "5.3.13", + "resolved": "https://registry.npmjs.org/node-sql-parser/-/node-sql-parser-5.3.13.tgz", + "integrity": "sha512-heyWv3lLjKHpcBDMUSR+R0DohRYZTYq+Ro3hJ4m9Ia8ccdKbL5UijIaWr2L4co+bmmFuvBVZ4v23QW2PqvBFAA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/pegjs": "^0.10.0", + "big-integer": "^1.6.48" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/npm-normalize-package-bin": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", @@ -5719,6 +5805,28 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-sql": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-sql/-/prettier-plugin-sql-0.19.2.tgz", + "integrity": "sha512-DAu1Jcanpvs32OAOXsqaVXOpPs4nFLVkB3XwzRiZZVNL5/c+XdlNxWFMiMpMhYhmCG5BW3srK8mhikCOv5tPfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsox": "^1.2.123", + "node-sql-parser": "^5.3.10", + "sql-formatter": "^15.6.5", + "tslib": "^2.8.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + }, + "peerDependencies": { + "prettier": "^3.0.3" + } + }, "node_modules/proc-log": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", @@ -5772,6 +5880,27 @@ ], "license": "MIT" }, + "node_modules/railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/react": { "version": "19.2.0", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", @@ -5895,6 +6024,16 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -6229,6 +6368,20 @@ "node": ">=0.10.0" } }, + "node_modules/sql-formatter": { + "version": "15.6.11", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.6.11.tgz", + "integrity": "sha512-T2aqOqwpGNqDvKNLJqsIVPhEgDCEp795GeXZg7AqbDTHmaEDBBAy/rwBd8lxnlYEjFm/DI+AsD7dnmJNhlUSWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "nearley": "^2.20.1" + }, + "bin": { + "sql-formatter": "bin/sql-formatter-cli.cjs" + } + }, "node_modules/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", diff --git a/package.json b/package.json index 357593c..6622171 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "eslint-config-next": "16.0.1", "eslint-config-prettier": "^10.1.8", "prettier": "^3.7.4", + "prettier-plugin-sql": "^0.19.2", "supabase": "^2.58.5", "tailwindcss": "^4", "typescript": "^5" From 2f63301152d7904fb43331ec737e73d325c3122d Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Tue, 16 Dec 2025 12:03:35 -0800 Subject: [PATCH 31/40] Apply Prettier --- .github/workflows/main.yaml | 2 +- .vscode/settings.json | 2 +- app/(pages)/example/page.tsx | 34 ++++++++------- app/(pages)/test-donations/page.tsx | 35 ++++++++-------- app/api/donations/actions.ts | 51 +++++++++++------------ app/api/example/actions.ts | 11 +++-- app/api/stores/action.ts | 12 +++--- app/components/test/donations.tsx | 35 ++++++++-------- app/globals.css | 2 +- app/layout.tsx | 12 +++--- app/lib/supabase/browser-client.ts | 6 +-- app/lib/supabase/server-client.ts | 12 +++--- app/page.tsx | 10 ++--- app/types/Donation.ts | 34 +++++++-------- app/types/ExampleType.ts | 2 +- app/types/Store.ts | 2 +- eslint.config.mjs | 16 ++++---- next.config.ts | 2 +- supabase/schemas/donations.sql | 64 ++++++++++++++--------------- supabase/schemas/example.sql | 19 ++++----- supabase/schemas/stores.sql | 37 +++++++---------- supabase/seeds/example.sql | 6 +-- 22 files changed, 198 insertions(+), 208 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 51cce57..361e7c8 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -21,4 +21,4 @@ jobs: version: latest - run: supabase link --project-ref $SUPABASE_PROJECT_ID - - run: supabase db push \ No newline at end of file + - run: supabase db push diff --git a/.vscode/settings.json b/.vscode/settings.json index f82fc39..9bf4d12 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true -} \ No newline at end of file +} diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx index 1caa104..24f824b 100644 --- a/app/(pages)/example/page.tsx +++ b/app/(pages)/example/page.tsx @@ -1,22 +1,22 @@ -"use client"; +'use client'; -import { createExampleEntry } from "@/app/api/example/actions"; -import { createStore } from "@/app/api/stores/action"; -import { ExampleType } from "@/app/types/ExampleType"; -import { Store } from "@/app/types/Store"; -import { deleteStore } from "@/app/api/stores/action"; -import TestDonationsPage from "../test-donations/page"; +import { createExampleEntry } from '@/app/api/example/actions'; +import { createStore } from '@/app/api/stores/action'; +import { ExampleType } from '@/app/types/ExampleType'; +import { Store } from '@/app/types/Store'; +import { deleteStore } from '@/app/api/stores/action'; +import TestDonationsPage from '../test-donations/page'; export default function Example() { const data: ExampleType = { id: 5, - name: "harry", + name: 'harry', }; const storeData: Store = { - store_id: "550e8400-e29b-41d4-a716-446655440000", - name: "test store", - street_address: "test address", + store_id: '550e8400-e29b-41d4-a716-446655440000', + name: 'test store', + street_address: 'test address', }; const handleExampleClick = async () => { @@ -34,9 +34,15 @@ export default function Example() {
Welcome to PATH! This is an example page.
- - - + + +
); diff --git a/app/(pages)/test-donations/page.tsx b/app/(pages)/test-donations/page.tsx index e12d11e..ab62d2c 100644 --- a/app/(pages)/test-donations/page.tsx +++ b/app/(pages)/test-donations/page.tsx @@ -1,41 +1,40 @@ -"use client"; +'use client'; -import { createDonation, deleteDonation } from "@/app/api/donations/actions"; +import { createDonation, deleteDonation } from '@/app/api/donations/actions'; export default function TestDonationsPage() { - const handleCreate = async () => { try { const donation = await createDonation({ - receiver_user_id: "00000000-0000-0000-0000-000000000000", + receiver_user_id: '00000000-0000-0000-0000-000000000000', store_id: null, donor_is_individual: true, - donor_individual_name: "Test User", - donor_business_name: "Donor Business Name", - donor_business_contact_name: "Ms. Donor", - donor_email: "test@example.com", - donor_phone: "123-456-7890", - donor_street_address: "1234 Bruin Ave", + donor_individual_name: 'Test User', + donor_business_name: 'Donor Business Name', + donor_business_contact_name: 'Ms. Donor', + donor_email: 'test@example.com', + donor_phone: '123-456-7890', + donor_street_address: '1234 Bruin Ave', donor_receive_mailings: false, donor_receive_emails: true, donor_remain_anonymous: false, estimated_value: 50, - items_donated: "Items donated!" + items_donated: 'Items donated!', }); - console.log("Created donation:", donation); + console.log('Created donation:', donation); } catch (err) { - console.error("Create failed:", err); + console.error('Create failed:', err); } }; const handleDelete = async () => { - const idToDelete = "b44b3b18-c499-4aec-9e73-aab1d3788d59"; // hard-coded ID for now + const idToDelete = 'b44b3b18-c499-4aec-9e73-aab1d3788d59'; // hard-coded ID for now try { const result = await deleteDonation(idToDelete); - console.log("Deleted donation:", result); + console.log('Deleted donation:', result); } catch (err) { - console.error("Delete failed:", err); + console.error('Delete failed:', err); } }; @@ -43,7 +42,9 @@ export default function TestDonationsPage() {

Test Donations

- +
); } diff --git a/app/api/donations/actions.ts b/app/api/donations/actions.ts index b73f018..f96d577 100644 --- a/app/api/donations/actions.ts +++ b/app/api/donations/actions.ts @@ -1,39 +1,38 @@ -"use server"; +'use server'; import { DonationInsert } from '@/app/types/Donation'; import { createClient } from '@/app/lib/supabase/server-client'; export async function createDonation(data: DonationInsert) { - const supabase = await createClient(); + const supabase = await createClient(); - const { data: inserted, error } = await supabase - .from("donations") - .insert(data) - .select() // after inserting, return full new row - .single(); // expect just one row, if more or less, throw an error + const { data: inserted, error } = await supabase + .from('donations') + .insert(data) + .select() // after inserting, return full new row + .single(); // expect just one row, if more or less, throw an error - if (error) { - console.error("Error creating donation: ", error); - throw new Error(error.message); - } + if (error) { + console.error('Error creating donation: ', error); + throw new Error(error.message); + } - return inserted; // return the row that was created + return inserted; // return the row that was created } // deletes donation based on donation_id export async function deleteDonation(donation_id: string) { - const supabase = await createClient(); - - const { error } = await supabase - .from("donations") - .delete() - .eq("donation_id", donation_id); - - if (error) { - console.error("Error deleting donation:", error); - throw new Error(error.message); - } - - return { success: true }; -} + const supabase = await createClient(); + + const { error } = await supabase + .from('donations') + .delete() + .eq('donation_id', donation_id); + if (error) { + console.error('Error deleting donation:', error); + throw new Error(error.message); + } + + return { success: true }; +} diff --git a/app/api/example/actions.ts b/app/api/example/actions.ts index db2c0cb..1d25eb6 100644 --- a/app/api/example/actions.ts +++ b/app/api/example/actions.ts @@ -1,14 +1,13 @@ -"use server"; +'use server'; -import { ExampleType } from "@/app/types/ExampleType"; -import { createClient } from "@/app/lib/supabase/server-client"; +import { ExampleType } from '@/app/types/ExampleType'; +import { createClient } from '@/app/lib/supabase/server-client'; export const createExampleEntry = async (data: ExampleType) => { const supabase = await createClient(); - const { data: entry, error } = await supabase.from("example").insert(data); + const { error } = await supabase.from('example').insert(data); if (error) { throw error; } - return entry; -} \ No newline at end of file +}; diff --git a/app/api/stores/action.ts b/app/api/stores/action.ts index 1e4ff33..54b4afe 100644 --- a/app/api/stores/action.ts +++ b/app/api/stores/action.ts @@ -1,12 +1,12 @@ -"use server"; +'use server'; -import { createClient } from "@/app/lib/supabase/server-client"; -import { StoreInsert } from "@/app/types/Store"; +import { createClient } from '@/app/lib/supabase/server-client'; +import { StoreInsert } from '@/app/types/Store'; // create store export const createStore = async (data: StoreInsert) => { const supabase = await createClient(); - const { data: entry, error } = await supabase.from("stores").insert(data); + const { data: entry, error } = await supabase.from('stores').insert(data); if (error) { throw error; } @@ -18,9 +18,9 @@ export const createStore = async (data: StoreInsert) => { export const deleteStore = async (store_id: string) => { const supabase = await createClient(); const { data: deletedData, error } = await supabase - .from("stores") + .from('stores') .delete() - .eq("store_id", store_id); + .eq('store_id', store_id); if (error) { throw error; } diff --git a/app/components/test/donations.tsx b/app/components/test/donations.tsx index 3d78fc2..9013cd4 100644 --- a/app/components/test/donations.tsx +++ b/app/components/test/donations.tsx @@ -1,41 +1,40 @@ -"use client"; +'use client'; -import { createDonation, deleteDonation } from "@/app/api/donations/actions"; +import { createDonation, deleteDonation } from '@/app/api/donations/actions'; export default function TestDonationsPage() { - const handleCreate = async () => { try { const donation = await createDonation({ - receiver_user_id: "00000000-0000-0000-0000-000000000000", + receiver_user_id: '00000000-0000-0000-0000-000000000000', store_id: null, donor_is_individual: true, - donor_individual_name: "Test User", - donor_business_name: "Donor Business Name", - donor_business_contact_name: "Ms. Donor", - donor_email: "test@example.com", - donor_phone: "123-456-7890", - donor_street_address: "1234 Bruin Ave", + donor_individual_name: 'Test User', + donor_business_name: 'Donor Business Name', + donor_business_contact_name: 'Ms. Donor', + donor_email: 'test@example.com', + donor_phone: '123-456-7890', + donor_street_address: '1234 Bruin Ave', donor_receive_mailings: false, donor_receive_emails: true, donor_remain_anonymous: false, estimated_value: 50, - items_donated: "Items donated!" + items_donated: 'Items donated!', }); - console.log("Created donation:", donation); + console.log('Created donation:', donation); } catch (err) { - console.error("Create failed:", err); + console.error('Create failed:', err); } }; const handleDelete = async () => { - const idToDelete = "5001e28d-91b6-4df8-8976-4c88c4ad112f"; // hard-coded ID for now + const idToDelete = '5001e28d-91b6-4df8-8976-4c88c4ad112f'; // hard-coded ID for now try { const result = await deleteDonation(idToDelete); - console.log("Deleted donation:", result); + console.log('Deleted donation:', result); } catch (err) { - console.error("Delete failed:", err); + console.error('Delete failed:', err); } }; @@ -43,7 +42,9 @@ export default function TestDonationsPage() {

Test Donations

- +
); } diff --git a/app/globals.css b/app/globals.css index dd085e1..581442c 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,4 +1,4 @@ -@import "tailwindcss"; +@import 'tailwindcss'; :root { --background: #ffffff; diff --git a/app/layout.tsx b/app/layout.tsx index 735d2b7..7095646 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,9 +1,9 @@ -import type { Metadata } from "next"; -import "./globals.css"; +import type { Metadata } from 'next'; +import './globals.css'; export const metadata: Metadata = { - title: "PATH App", - description: "Resource management application for PATH", + title: 'PATH App', + description: 'Resource management application for PATH', }; export default function RootLayout({ @@ -13,9 +13,7 @@ export default function RootLayout({ }>) { return ( - - {children} - + {children} ); } diff --git a/app/lib/supabase/browser-client.ts b/app/lib/supabase/browser-client.ts index 063c38d..7aab535 100644 --- a/app/lib/supabase/browser-client.ts +++ b/app/lib/supabase/browser-client.ts @@ -1,8 +1,8 @@ -import { createBrowserClient } from "@supabase/ssr"; +import { createBrowserClient } from '@supabase/ssr'; export function createClient() { return createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, ); -} \ No newline at end of file +} diff --git a/app/lib/supabase/server-client.ts b/app/lib/supabase/server-client.ts index 2d239fa..421518b 100644 --- a/app/lib/supabase/server-client.ts +++ b/app/lib/supabase/server-client.ts @@ -1,5 +1,5 @@ -import { createServerClient } from "@supabase/ssr"; -import { cookies } from "next/headers"; +import { createServerClient } from '@supabase/ssr'; +import { cookies } from 'next/headers'; export async function createClient() { const cookieStore = await cookies(); return createServerClient( @@ -13,13 +13,13 @@ export async function createClient() { setAll(cookiesToSet) { try { cookiesToSet.forEach(({ name, value, options }) => - cookieStore.set(name, value, options) + cookieStore.set(name, value, options), ); } catch { - throw new Error("Cookies are not available"); + throw new Error('Cookies are not available'); } }, }, - } + }, ); -} \ No newline at end of file +} diff --git a/app/page.tsx b/app/page.tsx index 1d25e78..e6c488e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,14 +1,14 @@ -"use client"; +'use client'; -import { useEffect } from "react"; -import { useRouter } from "next/navigation"; +import { useEffect } from 'react'; +import { useRouter } from 'next/navigation'; export default function Home() { const router = useRouter(); useEffect(() => { // replace() avoids adding to browser history - router.replace("/example"); + router.replace('/example'); }, [router]); return null; -} \ No newline at end of file +} diff --git a/app/types/Donation.ts b/app/types/Donation.ts index bd7e782..a9a2764 100644 --- a/app/types/Donation.ts +++ b/app/types/Donation.ts @@ -1,25 +1,25 @@ export type Donation = { - donation_id?: string; // optional, generated by database - receiver_user_id: string; - store_id?: string | null; + donation_id?: string; // optional, generated by database + receiver_user_id: string; + store_id?: string | null; - date_submitted?: Date | string; // generated by default - donor_is_individual: boolean; + date_submitted?: Date | string; // generated by default + donor_is_individual: boolean; - donor_individual_name?: string | null; // optional - donor_business_name?: string | null; // optional - donor_business_contact_name?: string | null; // optional + donor_individual_name?: string | null; // optional + donor_business_name?: string | null; // optional + donor_business_contact_name?: string | null; // optional - donor_email?: string | null; // optional - donor_phone?: string | null; // optional + donor_email?: string | null; // optional + donor_phone?: string | null; // optional - donor_street_address: string | null; // optional - donor_receive_mailings: boolean; - donor_receive_emails: boolean; - donor_remain_anonymous: boolean; + donor_street_address: string | null; // optional + donor_receive_mailings: boolean; + donor_receive_emails: boolean; + donor_remain_anonymous: boolean; - estimated_value: number; - items_donated: string; + estimated_value: number; + items_donated: string; }; -export type DonationInsert = Omit \ No newline at end of file +export type DonationInsert = Omit; diff --git a/app/types/ExampleType.ts b/app/types/ExampleType.ts index 42ecc61..69044bf 100644 --- a/app/types/ExampleType.ts +++ b/app/types/ExampleType.ts @@ -1,4 +1,4 @@ export type ExampleType = { id: number; name: string; -}; \ No newline at end of file +}; diff --git a/app/types/Store.ts b/app/types/Store.ts index a6268b6..0e2abcb 100644 --- a/app/types/Store.ts +++ b/app/types/Store.ts @@ -4,4 +4,4 @@ export type Store = { street_address: string; }; -export type StoreInsert = Omit \ No newline at end of file +export type StoreInsert = Omit; diff --git a/eslint.config.mjs b/eslint.config.mjs index 9f61ede..386b0bc 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,7 +1,7 @@ -import { defineConfig, globalIgnores } from "eslint/config"; -import nextVitals from "eslint-config-next/core-web-vitals"; -import nextTs from "eslint-config-next/typescript"; -import prettier from "eslint-config-prettier/flat"; +import { defineConfig, globalIgnores } from 'eslint/config'; +import nextVitals from 'eslint-config-next/core-web-vitals'; +import nextTs from 'eslint-config-next/typescript'; +import prettier from 'eslint-config-prettier/flat'; const eslintConfig = defineConfig([ ...nextVitals, @@ -10,10 +10,10 @@ const eslintConfig = defineConfig([ // Override default ignores of eslint-config-next. globalIgnores([ // Default ignores of eslint-config-next: - ".next/**", - "out/**", - "build/**", - "next-env.d.ts", + '.next/**', + 'out/**', + 'build/**', + 'next-env.d.ts', ]), ]); diff --git a/next.config.ts b/next.config.ts index e9ffa30..5e891cf 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,4 +1,4 @@ -import type { NextConfig } from "next"; +import type { NextConfig } from 'next'; const nextConfig: NextConfig = { /* config options here */ diff --git a/supabase/schemas/donations.sql b/supabase/schemas/donations.sql index 8cd726b..1a3612f 100644 --- a/supabase/schemas/donations.sql +++ b/supabase/schemas/donations.sql @@ -3,49 +3,47 @@ - Write RLS (row level security) policies following supabase/schemas/example.sql that enable unauthenticated users to view/update the table */ - create table donations ( - donation_id uuid default uuid_generate_v4() primary key, -- generated by default - receiver_user_id uuid not null, -- references users(id), -- linked to users table, required - store_id uuid, -- references stores(id), -- linked to stores table, optional - date_submitted timestamp with time zone default current_timestamp, -- generated by default - donor_is_individual boolean not null, -- required, used if donor is an individual - donor_individual_name text, -- optional, used if donor is an individual - donor_business_name text, -- optional, used if donor is a business - donor_business_contact_name text, -- optional, used if donor is a business - donor_email varchar(255), -- optional, can't store more than 255 characters - donor_phone varchar(20), -- optional, can't store more than 20 characters - donor_street_address text, -- optional, street address - donor_receive_mailings boolean not null, -- required, whether or not donor wants to receive mailings - donor_receive_emails boolean not null, -- required, whether or not donor wants to receive emails - donor_remain_anonymous boolean not null, -- required, whether or not donor wants to remain anonymous - estimated_value numeric not null, -- required, estimated value of donation in USD - items_donated text not null -- required, free-text description of items + donation_id uuid default uuid_generate_v4 () primary key, -- generated by default + receiver_user_id uuid not null, -- references users(id), -- linked to users table, required + store_id uuid, -- references stores(id), -- linked to stores table, optional + date_submitted timestamp with time zone default current_timestamp, -- generated by default + donor_is_individual boolean not null, -- required, used if donor is an individual + donor_individual_name text, -- optional, used if donor is an individual + donor_business_name text, -- optional, used if donor is a business + donor_business_contact_name text, -- optional, used if donor is a business + donor_email varchar(255), -- optional, can't store more than 255 characters + donor_phone varchar(20), -- optional, can't store more than 20 characters + donor_street_address text, -- optional, street address + donor_receive_mailings boolean not null, -- required, whether or not donor wants to receive mailings + donor_receive_emails boolean not null, -- required, whether or not donor wants to receive emails + donor_remain_anonymous boolean not null, -- required, whether or not donor wants to remain anonymous + estimated_value numeric not null, -- required, estimated value of donation in USD + items_donated text not null -- required, free-text description of items ); -- write RLS policies that enable select, insert, update, and delete across all unauthenticated users alter table donations enable row level security; -- select -create policy "public can read entries in donations" -on public.donations -for select to anon -- applies to anon role (anonymous/public users) -using (true); -- all rows satisfy condition, all rows are readable +create policy "public can read entries in donations" on public.donations for +select + to anon -- applies to anon role (anonymous/public users) + using (true); +-- all rows satisfy condition, all rows are readable -- insert -create policy "public can insert entries in donations" -on public.donations -for insert to anon -- applies to anon role (anonymous/public users) -with check (true); -- for insert/update row validation, only rows where condition evaluates to true can be inserted/updated +create policy "public can insert entries in donations" on public.donations for insert to anon -- applies to anon role (anonymous/public users) +with + check (true); +-- for insert/update row validation, only rows where condition evaluates to true can be inserted/updated -- update -create policy "public can update entries in donations" -on public.donations -for update to anon -with check(true); +create policy "public can update entries in donations" on public.donations +for update + to anon +with + check (true); -- delete -create policy "public can delete entries in donations" -on public.donations -for delete to anon -using (true); +create policy "public can delete entries in donations" on public.donations for delete to anon using (true); diff --git a/supabase/schemas/example.sql b/supabase/schemas/example.sql index ee8272c..01d7190 100644 --- a/supabase/schemas/example.sql +++ b/supabase/schemas/example.sql @@ -1,16 +1,11 @@ -create table "example" ( - "id" integer not null, - "name" text -); +create table "example" ("id" integer not null, "name" text); alter table example enable row level security; -create policy "public can read example" -on public.example -for select to anon -using (true); +create policy "public can read example" on public.example for +select + to anon using (true); -create policy "public can insert entries in example" -on public.example -for insert to anon -with check (true); \ No newline at end of file +create policy "public can insert entries in example" on public.example for insert to anon +with + check (true); diff --git a/supabase/schemas/stores.sql b/supabase/schemas/stores.sql index c30022e..063c91f 100644 --- a/supabase/schemas/stores.sql +++ b/supabase/schemas/stores.sql @@ -1,30 +1,23 @@ create table "stores" ( - store_id uuid default uuid_generate_v4() primary key, - name text, - street_address text + store_id uuid default uuid_generate_v4 () primary key, + name text, + street_address text ); alter table "stores" enable row level security; -create policy "public can read entries in stores" -on public.stores -for select to anon -using (true); +create policy "public can read entries in stores" on public.stores for +select + to anon using (true); -create policy "public can insert entries in stores" -on public.stores -for insert to anon -with check (true); - -create policy "public can update entries in stores" -on public.stores -for update to anon -using (true) -with check (true); - -create policy "public can delete entries in stores" -on public.stores -for delete to anon -using (true); +create policy "public can insert entries in stores" on public.stores for insert to anon +with + check (true); +create policy "public can update entries in stores" on public.stores +for update + to anon using (true) +with + check (true); +create policy "public can delete entries in stores" on public.stores for delete to anon using (true); diff --git a/supabase/seeds/example.sql b/supabase/seeds/example.sql index 16af5b8..f8b4f51 100644 --- a/supabase/seeds/example.sql +++ b/supabase/seeds/example.sql @@ -1,5 +1,5 @@ -insert into example - (id, name) +insert into + example (id, name) values (1, 'alice'), - (2, 'bob'); \ No newline at end of file + (2, 'bob'); From b9d10087dd62564292084d616a4bc5c7a65618f1 Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Tue, 16 Dec 2025 18:01:47 -0800 Subject: [PATCH 32/40] Apply Prettier and refactor code --- app/(pages)/example/page.tsx | 49 ------------------ app/(pages)/test/page.tsx | 17 +++++++ app/api/donations/actions.ts | 8 +-- app/api/example/actions.ts | 4 +- app/api/stores/{action.ts => actions.ts} | 14 +++--- .../test/DonationsTestComponent.tsx} | 4 +- app/components/test/ExampleTestComponent.tsx | 21 ++++++++ app/components/test/StoresTestComponent.tsx | 30 +++++++++++ app/components/test/donations.tsx | 50 ------------------- app/page.tsx | 2 +- app/types/{ExampleType.ts => example.ts} | 2 +- 11 files changed, 84 insertions(+), 117 deletions(-) delete mode 100644 app/(pages)/example/page.tsx create mode 100644 app/(pages)/test/page.tsx rename app/api/stores/{action.ts => actions.ts} (56%) rename app/{(pages)/test-donations/page.tsx => components/test/DonationsTestComponent.tsx} (92%) create mode 100644 app/components/test/ExampleTestComponent.tsx create mode 100644 app/components/test/StoresTestComponent.tsx delete mode 100644 app/components/test/donations.tsx rename app/types/{ExampleType.ts => example.ts} (54%) diff --git a/app/(pages)/example/page.tsx b/app/(pages)/example/page.tsx deleted file mode 100644 index 24f824b..0000000 --- a/app/(pages)/example/page.tsx +++ /dev/null @@ -1,49 +0,0 @@ -'use client'; - -import { createExampleEntry } from '@/app/api/example/actions'; -import { createStore } from '@/app/api/stores/action'; -import { ExampleType } from '@/app/types/ExampleType'; -import { Store } from '@/app/types/Store'; -import { deleteStore } from '@/app/api/stores/action'; -import TestDonationsPage from '../test-donations/page'; - -export default function Example() { - const data: ExampleType = { - id: 5, - name: 'harry', - }; - - const storeData: Store = { - store_id: '550e8400-e29b-41d4-a716-446655440000', - name: 'test store', - street_address: 'test address', - }; - - const handleExampleClick = async () => { - await createExampleEntry(data); - }; - - const handleAddStoreClick = async () => { - await createStore(storeData); - }; - - const handleDeleteStoreClick = async () => { - await deleteStore(storeData.store_id); - }; - return ( -
- Welcome to PATH! This is an example page. -
- - - - -
- ); -} diff --git a/app/(pages)/test/page.tsx b/app/(pages)/test/page.tsx new file mode 100644 index 0000000..f1be833 --- /dev/null +++ b/app/(pages)/test/page.tsx @@ -0,0 +1,17 @@ +'use client'; + +import ExampleTestComponent from '@/app/components/test/ExampleTestComponent'; +import DonationsTestComponent from '@/app/components/test/DonationsTestComponent'; +import StoresTestComponent from '@/app/components/test/StoresTestComponent'; + +export default function TestPage() { + return ( +
+ Welcome to PATH! This is a test page. +
+ + + +
+ ); +} diff --git a/app/api/donations/actions.ts b/app/api/donations/actions.ts index f96d577..5cc9cc6 100644 --- a/app/api/donations/actions.ts +++ b/app/api/donations/actions.ts @@ -1,6 +1,6 @@ 'use server'; -import { DonationInsert } from '@/app/types/Donation'; +import { DonationInsert } from '@/app/types/donation'; import { createClient } from '@/app/lib/supabase/server-client'; export async function createDonation(data: DonationInsert) { @@ -13,7 +13,7 @@ export async function createDonation(data: DonationInsert) { .single(); // expect just one row, if more or less, throw an error if (error) { - console.error('Error creating donation: ', error); + console.error('Error creating donation:', error); throw new Error(error.message); } @@ -21,13 +21,13 @@ export async function createDonation(data: DonationInsert) { } // deletes donation based on donation_id -export async function deleteDonation(donation_id: string) { +export async function deleteDonation(donationId: string) { const supabase = await createClient(); const { error } = await supabase .from('donations') .delete() - .eq('donation_id', donation_id); + .eq('donation_id', donationId); if (error) { console.error('Error deleting donation:', error); diff --git a/app/api/example/actions.ts b/app/api/example/actions.ts index 1d25eb6..159ae62 100644 --- a/app/api/example/actions.ts +++ b/app/api/example/actions.ts @@ -1,9 +1,9 @@ 'use server'; -import { ExampleType } from '@/app/types/ExampleType'; +import { Example } from '@/app/types/example'; import { createClient } from '@/app/lib/supabase/server-client'; -export const createExampleEntry = async (data: ExampleType) => { +export const createExampleEntry = async (data: Example) => { const supabase = await createClient(); const { error } = await supabase.from('example').insert(data); diff --git a/app/api/stores/action.ts b/app/api/stores/actions.ts similarity index 56% rename from app/api/stores/action.ts rename to app/api/stores/actions.ts index 54b4afe..db2e5ff 100644 --- a/app/api/stores/action.ts +++ b/app/api/stores/actions.ts @@ -1,28 +1,26 @@ 'use server'; import { createClient } from '@/app/lib/supabase/server-client'; -import { StoreInsert } from '@/app/types/Store'; +import { StoreInsert } from '@/app/types/store'; // create store export const createStore = async (data: StoreInsert) => { const supabase = await createClient(); - const { data: entry, error } = await supabase.from('stores').insert(data); + const { error } = await supabase.from('stores').insert(data); if (error) { throw error; } - return entry; }; // action deleteStore // delete store given store_id -export const deleteStore = async (store_id: string) => { +export const deleteStore = async (storeId: string) => { const supabase = await createClient(); - const { data: deletedData, error } = await supabase + const { error } = await supabase .from('stores') .delete() - .eq('store_id', store_id); + .eq('store_id', storeId); if (error) { throw error; } - return deletedData; -}; +}; \ No newline at end of file diff --git a/app/(pages)/test-donations/page.tsx b/app/components/test/DonationsTestComponent.tsx similarity index 92% rename from app/(pages)/test-donations/page.tsx rename to app/components/test/DonationsTestComponent.tsx index ab62d2c..97577a3 100644 --- a/app/(pages)/test-donations/page.tsx +++ b/app/components/test/DonationsTestComponent.tsx @@ -2,7 +2,7 @@ import { createDonation, deleteDonation } from '@/app/api/donations/actions'; -export default function TestDonationsPage() { +export default function DonationsTestComponent() { const handleCreate = async () => { try { const donation = await createDonation({ @@ -29,7 +29,7 @@ export default function TestDonationsPage() { }; const handleDelete = async () => { - const idToDelete = 'b44b3b18-c499-4aec-9e73-aab1d3788d59'; // hard-coded ID for now + const idToDelete = '40b188ab-e9a1-444d-9e6b-6b99d5017c90'; // hard-coded ID for now try { const result = await deleteDonation(idToDelete); console.log('Deleted donation:', result); diff --git a/app/components/test/ExampleTestComponent.tsx b/app/components/test/ExampleTestComponent.tsx new file mode 100644 index 0000000..0831c41 --- /dev/null +++ b/app/components/test/ExampleTestComponent.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { createExampleEntry } from '@/app/api/example/actions'; +import { Example } from '@/app/types/example'; + +export default function ExampleTestComponent() { + const data: Example = { + id: 5, + name: 'harry', + }; + + const handleExampleClick = async () => { + await createExampleEntry(data); + }; + + return ( + + ); +} diff --git a/app/components/test/StoresTestComponent.tsx b/app/components/test/StoresTestComponent.tsx new file mode 100644 index 0000000..a971406 --- /dev/null +++ b/app/components/test/StoresTestComponent.tsx @@ -0,0 +1,30 @@ +'use client'; + +import { createStore, deleteStore } from '@/app/api/stores/actions'; +import { StoreInsert } from '@/app/types/store'; + +export default function StoresTestComponent() { + const storeData: StoreInsert = { + name: 'test store', + street_address: 'test address', + }; + + const handleAddStoreClick = async () => { + await createStore(storeData); + }; + + const handleDeleteStoreClick = async () => { + await deleteStore('ea9a0fac-c881-4740-894c-34139859a057'); + }; + + return ( +
+ + +
+ ); +} diff --git a/app/components/test/donations.tsx b/app/components/test/donations.tsx deleted file mode 100644 index 9013cd4..0000000 --- a/app/components/test/donations.tsx +++ /dev/null @@ -1,50 +0,0 @@ -'use client'; - -import { createDonation, deleteDonation } from '@/app/api/donations/actions'; - -export default function TestDonationsPage() { - const handleCreate = async () => { - try { - const donation = await createDonation({ - receiver_user_id: '00000000-0000-0000-0000-000000000000', - store_id: null, - donor_is_individual: true, - donor_individual_name: 'Test User', - donor_business_name: 'Donor Business Name', - donor_business_contact_name: 'Ms. Donor', - donor_email: 'test@example.com', - donor_phone: '123-456-7890', - donor_street_address: '1234 Bruin Ave', - donor_receive_mailings: false, - donor_receive_emails: true, - donor_remain_anonymous: false, - estimated_value: 50, - items_donated: 'Items donated!', - }); - - console.log('Created donation:', donation); - } catch (err) { - console.error('Create failed:', err); - } - }; - - const handleDelete = async () => { - const idToDelete = '5001e28d-91b6-4df8-8976-4c88c4ad112f'; // hard-coded ID for now - try { - const result = await deleteDonation(idToDelete); - console.log('Deleted donation:', result); - } catch (err) { - console.error('Delete failed:', err); - } - }; - - return ( -
-

Test Donations

- - -
- ); -} diff --git a/app/page.tsx b/app/page.tsx index e6c488e..050e73b 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -7,7 +7,7 @@ export default function Home() { const router = useRouter(); useEffect(() => { // replace() avoids adding to browser history - router.replace('/example'); + router.replace('/test'); }, [router]); return null; diff --git a/app/types/ExampleType.ts b/app/types/example.ts similarity index 54% rename from app/types/ExampleType.ts rename to app/types/example.ts index 69044bf..3e3a313 100644 --- a/app/types/ExampleType.ts +++ b/app/types/example.ts @@ -1,4 +1,4 @@ -export type ExampleType = { +export type Example = { id: number; name: string; }; From 3beec205df49a26f50cd46f936fdab081dc7377d Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Tue, 16 Dec 2025 20:18:42 -0800 Subject: [PATCH 33/40] Rename type files --- app/types/{Donation.ts => donation.ts} | 0 app/types/{Store.ts => store.ts} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename app/types/{Donation.ts => donation.ts} (100%) rename app/types/{Store.ts => store.ts} (100%) diff --git a/app/types/Donation.ts b/app/types/donation.ts similarity index 100% rename from app/types/Donation.ts rename to app/types/donation.ts diff --git a/app/types/Store.ts b/app/types/store.ts similarity index 100% rename from app/types/Store.ts rename to app/types/store.ts From 5c2a0789a33905f6e99594158c56120220f3a929 Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Tue, 16 Dec 2025 21:22:04 -0800 Subject: [PATCH 34/40] Add TicketItemsTestComponent and apply Prettier --- app/(pages)/test/page.tsx | 2 + app/api/stores/actions.ts | 2 +- app/api/ticket-items/action.ts | 64 ------------------- app/api/ticket-items/actions.ts | 57 +++++++++++++++++ .../test/TicketItemsTestComponent.tsx | 48 ++++++++++++++ app/types/Ticket.ts | 2 +- supabase/schemas/ticket_items.sql | 58 ++++++++--------- 7 files changed, 135 insertions(+), 98 deletions(-) delete mode 100644 app/api/ticket-items/action.ts create mode 100644 app/api/ticket-items/actions.ts create mode 100644 app/components/test/TicketItemsTestComponent.tsx diff --git a/app/(pages)/test/page.tsx b/app/(pages)/test/page.tsx index f1be833..d4e76dd 100644 --- a/app/(pages)/test/page.tsx +++ b/app/(pages)/test/page.tsx @@ -3,6 +3,7 @@ import ExampleTestComponent from '@/app/components/test/ExampleTestComponent'; import DonationsTestComponent from '@/app/components/test/DonationsTestComponent'; import StoresTestComponent from '@/app/components/test/StoresTestComponent'; +import TicketItemsTestComponent from '@/app/components/test/TicketItemsTestComponent'; export default function TestPage() { return ( @@ -12,6 +13,7 @@ export default function TestPage() { + ); } diff --git a/app/api/stores/actions.ts b/app/api/stores/actions.ts index db2e5ff..2aa6b59 100644 --- a/app/api/stores/actions.ts +++ b/app/api/stores/actions.ts @@ -23,4 +23,4 @@ export const deleteStore = async (storeId: string) => { if (error) { throw error; } -}; \ No newline at end of file +}; diff --git a/app/api/ticket-items/action.ts b/app/api/ticket-items/action.ts deleted file mode 100644 index 911d4c0..0000000 --- a/app/api/ticket-items/action.ts +++ /dev/null @@ -1,64 +0,0 @@ -"use server"; - -import { TicketItemInsert } from '@/app/types/Ticket'; -import { createClient } from "@/app/lib/supabase/server-client"; - -export async function createTicketItem(ticketItemData) { - const supabase = await createClient(); - // Server Action uses a mutation method - const { error } = await supabase.from("ticket_items").insert({ - // ticket_item_id: removed since db auto generates - ticket_id: ticketItemData.get("ticket_id"), - inventory_item_id: ticketItemData.get("inventory_item_id") ?? null, - free_text_description: ticketItemData.get("free_text_description") ?? null, - quantity_requested: ticketItemData.get("quantity_requested") ?? null, - is_in_stock_request: ticketItemData.get("is_in_stock_request"), - }); - //.select().single() <- kept only for debugging to see inserted entry - if (error) { - console.error("Error inserting ticket item:", error); - throw new Error(error.message); - } -} - -export async function changeTicketItemQuantity( - ticketItemId: string, - newQuantity: number | null -) { - const supabase = await createClient(); - const { error } = await supabase - .from("ticket_items") - .update({ quantity_requested: newQuantity }) - .eq("ticket_item_id", ticketItemId); - if (error) { - console.log("Error updating ticket item:", error); - throw new Error(error.message); - } -} - -export async function changeTicketItemDescription( - ticket_item_id: string, - newDescription: string -) { - const supabase = await createClient(); - const { error } = await supabase - .from("ticket_items") - .update({ free_text_description: newDescription }) - .eq("ticket_item_id", ticket_item_id); - if (error) { - console.log("Error updating ticket free text description:", error); - throw new Error(error.message); - } -} - -export async function deleteTicketItem(ticket_item_id: string) { - const supabase = await createClient(); - const { error } = await supabase - .from("ticket_items") - .delete() - .eq("ticket_item_id", ticket_item_id); - if (error) { - console.log("Error deleting ticket:", error); - throw new Error(error.message); - } -} diff --git a/app/api/ticket-items/actions.ts b/app/api/ticket-items/actions.ts new file mode 100644 index 0000000..a2365e1 --- /dev/null +++ b/app/api/ticket-items/actions.ts @@ -0,0 +1,57 @@ +'use server'; + +import { TicketItemInsert } from '@/app/types/ticket'; +import { createClient } from '@/app/lib/supabase/server-client'; + +export async function createTicketItem(ticketItemData: TicketItemInsert) { + const supabase = await createClient(); + // Server Action uses a mutation method + const { error } = await supabase.from('ticket_items').insert(ticketItemData); + //.select().single(); <- kept only for debugging to see inserted entry + if (error) { + console.error('Error inserting ticket item:', error); + throw new Error(error.message); + } +} + +export async function changeTicketItemQuantity( + ticketItemId: string, + newQuantity: number | null, +) { + const supabase = await createClient(); + const { error } = await supabase + .from('ticket_items') + .update({ quantity_requested: newQuantity }) + .eq('ticket_item_id', ticketItemId); + if (error) { + console.log('Error updating ticket item:', error); + throw new Error(error.message); + } +} + +export async function changeTicketItemDescription( + ticketItemId: string, + newDescription: string, +) { + const supabase = await createClient(); + const { error } = await supabase + .from('ticket_items') + .update({ free_text_description: newDescription }) + .eq('ticket_item_id', ticketItemId); + if (error) { + console.log('Error updating ticket free text description:', error); + throw new Error(error.message); + } +} + +export async function deleteTicketItem(ticketItemId: string) { + const supabase = await createClient(); + const { error } = await supabase + .from('ticket_items') + .delete() + .eq('ticket_item_id', ticketItemId); + if (error) { + console.log('Error deleting ticket:', error); + throw new Error(error.message); + } +} diff --git a/app/components/test/TicketItemsTestComponent.tsx b/app/components/test/TicketItemsTestComponent.tsx new file mode 100644 index 0000000..fe7929a --- /dev/null +++ b/app/components/test/TicketItemsTestComponent.tsx @@ -0,0 +1,48 @@ +'use client'; + +import { + createTicketItem, + changeTicketItemDescription, + changeTicketItemQuantity, + deleteTicketItem, +} from '@/app/api/ticket-items/actions'; +import { TicketItemInsert } from '@/app/types/ticket'; + +export default function TicketItemTestComponent() { + const data: TicketItemInsert = { + ticket_id: 'c088b0b7-e4c3-40ae-80ca-9d8376dbb4fa', + inventory_item_id: 'bec1fdd9-d6f0-4c49-aa2f-c67a18fc05f2', + free_text_description: 'sample description', + quantity_requested: 10, + is_in_stock_request: true, + }; + + const ticketItemId = '4a3e0f98-e17a-4891-9d7c-d72f44245adc'; + + const handleCreate = async () => { + await createTicketItem(data); + }; + + const handleDelete = async () => { + await deleteTicketItem(ticketItemId); + }; + + const handleChangeDescription = async () => { + await changeTicketItemDescription(ticketItemId, 'new description'); + }; + + const handleChangeQuantity = async () => { + await changeTicketItemQuantity(ticketItemId, 5); + }; + + return ( +
+ + + + +
+ ); +} diff --git a/app/types/Ticket.ts b/app/types/Ticket.ts index ef8b1c5..7818a30 100644 --- a/app/types/Ticket.ts +++ b/app/types/Ticket.ts @@ -9,4 +9,4 @@ export type TicketItem = { export type TicketItemUpdate = Partial; -export type TicketItemInsert = Omit +export type TicketItemInsert = Omit; diff --git a/supabase/schemas/ticket_items.sql b/supabase/schemas/ticket_items.sql index 93a1171..2857d30 100644 --- a/supabase/schemas/ticket_items.sql +++ b/supabase/schemas/ticket_items.sql @@ -1,38 +1,32 @@ -CREATE TABLE ticket_items -( - ticket_item_id UUID default uuid_generate_v4() primary key, - ticket_id UUID not null, - inventory_item_id UUID, - free_text_description TEXT, - quantity_requested INT, - is_in_stock_request BOOLEAN not null - - /* CONSTRAINT fk_tickets - FOREIGN KEY ticket_id - REFERENCES tickets(ticket_id) - CONSTRAINT fk_inventory_items - FOREIGN KEY inventory_item_id - REFERENCES inventory_items(inventory_item_id)*/ +create table ticket_items ( + ticket_item_id uuid default uuid_generate_v4 () primary key, + ticket_id uuid not null, + inventory_item_id uuid, + free_text_description text, + quantity_requested int, + is_in_stock_request boolean not null + /* CONSTRAINT fk_tickets + FOREIGN KEY ticket_id + REFERENCES tickets(ticket_id) + CONSTRAINT fk_inventory_items + FOREIGN KEY inventory_item_id + REFERENCES inventory_items(inventory_item_id)*/ ); + alter table "ticket_items" enable row level security; -create policy "public can read ticket_items" -on public.ticket_items -for select to anon -using (true); +create policy "public can read ticket_items" on public.ticket_items for +select + to anon using (true); -create policy "public can insert ticket_items" -on public.ticket_items -for insert to anon -with check (true); +create policy "public can insert ticket_items" on public.ticket_items for insert to anon +with + check (true); -create policy "public can update ticket_items" -on public.ticket_items -for update to anon -using (true) -with check (true); +create policy "public can update ticket_items" on public.ticket_items +for update + to anon using (true) +with + check (true); -create policy "public can delete ticket_items" -on public.ticket_items -for delete to anon -using (true); \ No newline at end of file +create policy "public can delete ticket_items" on public.ticket_items for delete to anon using (true); From bcc86719c13365ad3e9a98789d25d2087dae631c Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Tue, 16 Dec 2025 21:37:37 -0800 Subject: [PATCH 35/40] Update GitHub Actions workflow --- .github/workflows/{main.yaml => update-remote-database.yaml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{main.yaml => update-remote-database.yaml} (87%) diff --git a/.github/workflows/main.yaml b/.github/workflows/update-remote-database.yaml similarity index 87% rename from .github/workflows/main.yaml rename to .github/workflows/update-remote-database.yaml index 361e7c8..93e8996 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/update-remote-database.yaml @@ -1,4 +1,4 @@ -name: Release (Production) +name: Update Remote Database on: push: @@ -21,4 +21,4 @@ jobs: version: latest - run: supabase link --project-ref $SUPABASE_PROJECT_ID - - run: supabase db push + - run: supabase db push --include-all From 86724da343812abf809506a0d7ae777a2035a5d8 Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Tue, 16 Dec 2025 21:37:59 -0800 Subject: [PATCH 36/40] Rename type file --- app/types/{Ticket.ts => ticket.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/types/{Ticket.ts => ticket.ts} (100%) diff --git a/app/types/Ticket.ts b/app/types/ticket.ts similarity index 100% rename from app/types/Ticket.ts rename to app/types/ticket.ts From af7bfa2369de36f076f35cebfd1a034df83ebf62 Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Tue, 16 Dec 2025 23:23:57 -0800 Subject: [PATCH 37/40] Refactor code into InventoryItemsTestComponent and apply Prettier --- app/(pages)/test-inventory-items/page.tsx | 89 ------------------ app/(pages)/test/page.tsx | 2 + app/api/inventory-items/actions.ts | 70 +++++++-------- .../test/InventoryItemsTestComponent.tsx | 90 +++++++++++++++++++ app/types/InventoryItem.ts | 16 ---- app/types/inventory.ts | 14 +++ supabase/schemas/inventory_items.sql | 33 +++---- 7 files changed, 155 insertions(+), 159 deletions(-) delete mode 100644 app/(pages)/test-inventory-items/page.tsx create mode 100644 app/components/test/InventoryItemsTestComponent.tsx delete mode 100644 app/types/InventoryItem.ts create mode 100644 app/types/inventory.ts diff --git a/app/(pages)/test-inventory-items/page.tsx b/app/(pages)/test-inventory-items/page.tsx deleted file mode 100644 index 8a867dd..0000000 --- a/app/(pages)/test-inventory-items/page.tsx +++ /dev/null @@ -1,89 +0,0 @@ -"use client"; -import { useState } from "react"; -import type { InventoryType } from "@/app/types/InventoryItem"; -import { createItem, changeItemQuantity, deleteItem} from "@/app/api/inventory-items/actions"; - -export default function TestInventoryItemPage() { - const [inventoryId, setInventoryId] = useState(""); - const [inventoryIdToDelete, setInventoryIdToDelete] = useState(""); - const [updatedQuantity, setUpdatedQuantity] = useState(0); - - const data: InventoryType = { - store_id: "4c7b2b65-16df-44b5-a3c2-e2fcd090b76c", - subcategory: 1, - item: "Sample Item", - description: "test description", - photo_url: "http://example.com/photo.jpg", - quantity_available: 2, - is_hidden: false, - }; - - const handleCreateItem = async () => { - await createItem(data); - } - - const handleChangeItemQuantity = async () => { - await changeItemQuantity(inventoryId, Number(updatedQuantity)); - } - - const handleDeleteItem = async () => { - await deleteItem(inventoryIdToDelete); - } - - return ( -
-
-

Create an Item

- -
-
-

Update an Item

-
- -
- -
- -
-
-

Delete an Item

-
- -
- -
-
-
- ); - } \ No newline at end of file diff --git a/app/(pages)/test/page.tsx b/app/(pages)/test/page.tsx index d4e76dd..4cd76b0 100644 --- a/app/(pages)/test/page.tsx +++ b/app/(pages)/test/page.tsx @@ -4,6 +4,7 @@ import ExampleTestComponent from '@/app/components/test/ExampleTestComponent'; import DonationsTestComponent from '@/app/components/test/DonationsTestComponent'; import StoresTestComponent from '@/app/components/test/StoresTestComponent'; import TicketItemsTestComponent from '@/app/components/test/TicketItemsTestComponent'; +import InventoryItemsTestComponent from '@/app/components/test/InventoryItemsTestComponent'; export default function TestPage() { return ( @@ -14,6 +15,7 @@ export default function TestPage() { + ); } diff --git a/app/api/inventory-items/actions.ts b/app/api/inventory-items/actions.ts index 87a7ef4..3a67cfa 100644 --- a/app/api/inventory-items/actions.ts +++ b/app/api/inventory-items/actions.ts @@ -1,40 +1,40 @@ -"use server"; +'use server'; -import { InventoryType } from "@/app/types/InventoryItem"; -import { createClient } from "@/app/lib/supabase/server-client"; +import { InventoryItemInsert } from '@/app/types/inventory'; +import { createClient } from '@/app/lib/supabase/server-client'; -export const createItem = async (data: InventoryType) => { - const supabase = await createClient(); - const { data: entry, error } = await supabase.from("inventory_items").insert(data); - - if (error) { - throw error; - } - return entry; -} +export const createItem = async (data: InventoryItemInsert) => { + const supabase = await createClient(); + const { error } = await supabase.from('inventory_items').insert(data); -export const changeItemQuantity = async (inventoryId: string, updatedAvailableQuantity: number) => { - const supabase = await createClient(); - const { data: entry, error } = await supabase - .from("inventory_items") - .update({ quantity_available: updatedAvailableQuantity }) - .eq("inventory_item_id", inventoryId); - - if (error) { - throw error; - } - return entry; -} + if (error) { + throw error; + } +}; + +export const changeItemQuantity = async ( + inventoryId: string, + updatedAvailableQuantity: number, +) => { + const supabase = await createClient(); + const { error } = await supabase + .from('inventory_items') + .update({ quantity_available: updatedAvailableQuantity }) + .eq('inventory_item_id', inventoryId); + + if (error) { + throw error; + } +}; export const deleteItem = async (inventoryId: string) => { - const supabase = await createClient(); - const { data: entry, error } = await supabase - .from("inventory_items") - .delete() - .eq("inventory_item_id", inventoryId); - - if (error) { - throw error; - } - return entry; - } \ No newline at end of file + const supabase = await createClient(); + const { error } = await supabase + .from('inventory_items') + .delete() + .eq('inventory_item_id', inventoryId); + + if (error) { + throw error; + } +}; diff --git a/app/components/test/InventoryItemsTestComponent.tsx b/app/components/test/InventoryItemsTestComponent.tsx new file mode 100644 index 0000000..c5915a2 --- /dev/null +++ b/app/components/test/InventoryItemsTestComponent.tsx @@ -0,0 +1,90 @@ +'use client'; + +import { useState } from 'react'; +import type { InventoryItemInsert } from '@/app/types/inventory'; +import { + createItem, + changeItemQuantity, + deleteItem, +} from '@/app/api/inventory-items/actions'; + +export default function InventoryItemsTestComponent() { + const [inventoryId, setInventoryId] = useState(''); + const [inventoryIdToDelete, setInventoryIdToDelete] = useState(''); + const [updatedQuantity, setUpdatedQuantity] = useState(0); + + const data: InventoryItemInsert = { + store_id: '4c7b2b65-16df-44b5-a3c2-e2fcd090b76c', + subcategory: 1, + item: 'Sample Item', + description: 'test description', + photo_url: 'http://example.com/photo.jpg', + quantity_available: 2, + is_hidden: false, + }; + + const handleCreateItem = async () => { + await createItem(data); + }; + + const handleChangeItemQuantity = async () => { + await changeItemQuantity(inventoryId, Number(updatedQuantity)); + }; + + const handleDeleteItem = async () => { + await deleteItem(inventoryIdToDelete); + }; + + return ( +
+
+

Create an Item

+ +
+
+

Update an Item

+
+ +
+ +
+ +
+
+

Delete an Item

+
+ +
+ +
+
+
+ ); +} diff --git a/app/types/InventoryItem.ts b/app/types/InventoryItem.ts deleted file mode 100644 index 53f7391..0000000 --- a/app/types/InventoryItem.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Create a type InventoryItem corresponding the table. -// For the uuid PostgreSQL data type, you can use a TypeScrypt string. -import { UUID } from "crypto" - -export type InventoryType = { - inventory_item_id?: string; // UUID - store_id: string; // UUID - subcategory: number; - item: string; - description: string; - photo_url: string; - quantity_available: number; - is_hidden: boolean; -}; - -export type InventoryItemUpdate = Partial; \ No newline at end of file diff --git a/app/types/inventory.ts b/app/types/inventory.ts new file mode 100644 index 0000000..47fe9ea --- /dev/null +++ b/app/types/inventory.ts @@ -0,0 +1,14 @@ +export type InventoryItem = { + inventory_item_id?: string; // UUID + store_id: string; // UUID + subcategory: number; + item: string; + description: string; + photo_url: string; + quantity_available: number; + is_hidden: boolean; +}; + +export type InventoryItemUpdate = Partial; + +export type InventoryItemInsert = Omit; diff --git a/supabase/schemas/inventory_items.sql b/supabase/schemas/inventory_items.sql index 8b1fee1..5c3d158 100644 --- a/supabase/schemas/inventory_items.sql +++ b/supabase/schemas/inventory_items.sql @@ -1,6 +1,6 @@ create table "inventory_items" ( - "inventory_item_id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - "store_id" UUID not null, + "inventory_item_id" uuid primary key default uuid_generate_v4 (), + "store_id" uuid not null, "subcategory" int not null, "item" varchar(255) not null, "description" text not null, @@ -11,23 +11,18 @@ create table "inventory_items" ( alter table "inventory_items" enable row level security; -create policy "public can select entries in inventory_items" -on public.inventory_items -for select to anon -using (true); +create policy "public can select entries in inventory_items" on public.inventory_items for +select + to anon using (true); -create policy "public can insert entries in inventory_items" -on public.inventory_items -for insert to anon -with check (true); +create policy "public can insert entries in inventory_items" on public.inventory_items for insert to anon +with + check (true); -create policy "public can update entries in inventory_items" -on public.inventory_items -for update to anon -using (true) -with check (true); +create policy "public can update entries in inventory_items" on public.inventory_items +for update + to anon using (true) +with + check (true); -create policy "public can delete entries in inventory_items" -on public.inventory_items -for delete to anon -using (true); \ No newline at end of file +create policy "public can delete entries in inventory_items" on public.inventory_items for delete to anon using (true); From 2fa9eb2416ac300d68d548687c170f9de3ba9d38 Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Wed, 17 Dec 2025 15:49:58 -0800 Subject: [PATCH 38/40] Refactor code into TicketsTestComponent and apply Prettier --- app/(pages)/test/page.tsx | 6 +- app/api/tickets/actions.ts | 56 ++++++------- app/components/test/TicketsTestComponent.tsx | 83 ++++++++++++++++++++ app/types/Ticket.d.ts | 9 --- app/types/ticket.ts | 12 +++ supabase/schemas/tickets.sql | 46 +++++------ 6 files changed, 148 insertions(+), 64 deletions(-) create mode 100644 app/components/test/TicketsTestComponent.tsx delete mode 100644 app/types/Ticket.d.ts diff --git a/app/(pages)/test/page.tsx b/app/(pages)/test/page.tsx index 4cd76b0..3a3df0a 100644 --- a/app/(pages)/test/page.tsx +++ b/app/(pages)/test/page.tsx @@ -3,8 +3,9 @@ import ExampleTestComponent from '@/app/components/test/ExampleTestComponent'; import DonationsTestComponent from '@/app/components/test/DonationsTestComponent'; import StoresTestComponent from '@/app/components/test/StoresTestComponent'; -import TicketItemsTestComponent from '@/app/components/test/TicketItemsTestComponent'; import InventoryItemsTestComponent from '@/app/components/test/InventoryItemsTestComponent'; +import TicketsTestComponent from '@/app/components/test/TicketsTestComponent'; +import TicketItemsTestComponent from '@/app/components/test/TicketItemsTestComponent'; export default function TestPage() { return ( @@ -14,8 +15,9 @@ export default function TestPage() { - + + ); } diff --git a/app/api/tickets/actions.ts b/app/api/tickets/actions.ts index b6a4bd5..198b3cc 100644 --- a/app/api/tickets/actions.ts +++ b/app/api/tickets/actions.ts @@ -1,37 +1,39 @@ -"use server"; // directive that indicates that the functions we define are server actions +'use server'; // directive that indicates that the functions we define are server actions -import { Ticket } from "@/app/types/Ticket"; -import { createClient } from "@/app/lib/supabase/server-client"; +import { TicketInsert } from '@/app/types/ticket'; +import { createClient } from '@/app/lib/supabase/server-client'; -export async function createTicket(formData: Ticket) { - const supabase = await createClient(); - // pulling out the error from the insert into tickets table - const { error: err } = await supabase.from("tickets").insert({ - requestor_user_id: formData.requestor_user_id, - store_id: formData.store_id, - status: formData.status, - }); +export async function createTicket(formData: TicketInsert) { + const supabase = await createClient(); + // pulling out the error from the insert into tickets table + const { error: err } = await supabase.from('tickets').insert(formData); - if (err) { - return { success: false, error: err }; - } - return { success: true }; + if (err) { + return { success: false, error: err }; + } + return { success: true }; } export async function deleteTicket(ticketId: string) { - const supabase = await createClient(); - const { error: err } = await supabase.from("tickets").delete().eq("ticket_id", ticketId); - if (err) { - return { success: false, error: err }; - } - return { success: true }; + const supabase = await createClient(); + const { error: err } = await supabase + .from('tickets') + .delete() + .eq('ticket_id', ticketId); + if (err) { + return { success: false, error: err }; + } + return { success: true }; } export async function updateTicketStatus(newStatus: string, ticketId: string) { - const supabase = await createClient(); - const { error: err } = await supabase.from("tickets").update({status: newStatus }).eq("ticket_id", ticketId); - if (err) { - return { success : false, error: err}; - } - return { success : true }; + const supabase = await createClient(); + const { error: err } = await supabase + .from('tickets') + .update({ status: newStatus }) + .eq('ticket_id', ticketId); + if (err) { + return { success: false, error: err }; + } + return { success: true }; } diff --git a/app/components/test/TicketsTestComponent.tsx b/app/components/test/TicketsTestComponent.tsx new file mode 100644 index 0000000..e4cb22d --- /dev/null +++ b/app/components/test/TicketsTestComponent.tsx @@ -0,0 +1,83 @@ +'use client'; + +import { + createTicket, + deleteTicket, + updateTicketStatus, +} from '@/app/api/tickets/actions'; +import { useState } from 'react'; +import { TicketInsert } from '@/app/types/ticket'; + +export default function TicketsTestComponent() { + const [ticketToDelete, setDelete] = useState(''); + const [ticketToUpdate, setUpdateTicket] = useState(''); + const [updateStatus, setStatus] = useState(''); + + const ticketData: TicketInsert = { + requestor_user_id: '4c4f3502-1b31-4040-8a7a-2baedbc8a347', + store_id: '94b6329f-7383-46e2-978d-e105d31c3813', + status: 'ready', + }; + + const submitTicket = async () => { + await createTicket(ticketData); + }; + + const sendTicketDeletion = async () => { + await deleteTicket(ticketToDelete); + }; + + const updateTicket = async () => { + await updateTicketStatus(updateStatus, ticketToUpdate); + }; + + return ( +
+

Ticket Testing

+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ ); +} diff --git a/app/types/Ticket.d.ts b/app/types/Ticket.d.ts deleted file mode 100644 index 3d16954..0000000 --- a/app/types/Ticket.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type Ticket = { - ticket_id: string; // string corresponds to uuid - requestor_user_id: string; - store_id: string; - status: string; // string corresponds to varchar - date_submitted: Date | string; // Date | string corresponds to time stamp with time zone -}; - -export type TicketUpdate = Partial; diff --git a/app/types/ticket.ts b/app/types/ticket.ts index 7818a30..92f5b7c 100644 --- a/app/types/ticket.ts +++ b/app/types/ticket.ts @@ -1,3 +1,15 @@ +export type Ticket = { + ticket_id: string; // string corresponds to uuid + requestor_user_id: string; + store_id: string; + status: string; // string corresponds to varchar + date_submitted: Date | string; // Date | string corresponds to time stamp with time zone +}; + +export type TicketUpdate = Partial; + +export type TicketInsert = Omit; + export type TicketItem = { ticket_item_id: string; ticket_id: string; diff --git a/supabase/schemas/tickets.sql b/supabase/schemas/tickets.sql index 4db953e..7d5f427 100644 --- a/supabase/schemas/tickets.sql +++ b/supabase/schemas/tickets.sql @@ -1,35 +1,29 @@ -create type ticket_status as enum ('requested', 'ready', 'rejected', 'fulfilled'); +create type ticket_status as enum('requested', 'ready', 'rejected', 'fulfilled'); create table "tickets" ( - "ticket_id" uuid default uuid_generate_v4() primary key, - "requestor_user_id" uuid not null, - "store_id" uuid not null, - "status" ticket_status not null, -- max 50 character status - "date_submitted" TIMESTAMP WITH TIME ZONE default now() - -- FOREIGN KEY (requestor_user_id) REFERENCES users(user_id), - -- FOREIGN KEY (store_id) REFERENCES stores(store_id) + "ticket_id" uuid default uuid_generate_v4 () primary key, + "requestor_user_id" uuid not null, + "store_id" uuid not null, + "status" ticket_status not null, -- max 50 character status + "date_submitted" timestamp with time zone default now() + -- FOREIGN KEY (requestor_user_id) REFERENCES users(user_id), + -- FOREIGN KEY (store_id) REFERENCES stores(store_id) ); alter table tickets enable row level security; -create policy "public can read entries in tickets" -on public.tickets -for select to anon -using (true); +create policy "public can read entries in tickets" on public.tickets for +select + to anon using (true); -create policy "public can insert entries in tickets" -on public.tickets -for insert to anon -with check (true); +create policy "public can insert entries in tickets" on public.tickets for insert to anon +with + check (true); -create policy "public can update entries in tickets" -on public.tickets -for update to anon -using (true) -with check (true); - -create policy "public can delete entries in tickets" -on public.tickets -for delete to anon -using (true); +create policy "public can update entries in tickets" on public.tickets +for update + to anon using (true) +with + check (true); +create policy "public can delete entries in tickets" on public.tickets for delete to anon using (true); From 548dfe579189589158e84a171bcd79a9f2fb7d86 Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Wed, 17 Dec 2025 19:54:07 -0800 Subject: [PATCH 39/40] Make return behavior of Server Actions consistent --- app/api/donations/actions.ts | 20 ++++++------ app/api/example/actions.ts | 12 ++++++-- app/api/inventory-items/actions.ts | 40 +++++++++++++++--------- app/api/stores/actions.ts | 23 +++++++++----- app/api/ticket-items/actions.ts | 49 ++++++++++++++++++------------ app/api/tickets/actions.ts | 15 +++++++-- 6 files changed, 101 insertions(+), 58 deletions(-) diff --git a/app/api/donations/actions.ts b/app/api/donations/actions.ts index 5cc9cc6..2260a0b 100644 --- a/app/api/donations/actions.ts +++ b/app/api/donations/actions.ts @@ -6,33 +6,31 @@ import { createClient } from '@/app/lib/supabase/server-client'; export async function createDonation(data: DonationInsert) { const supabase = await createClient(); - const { data: inserted, error } = await supabase + const { data: entry, error: err } = await supabase .from('donations') .insert(data) .select() // after inserting, return full new row .single(); // expect just one row, if more or less, throw an error - if (error) { - console.error('Error creating donation:', error); - throw new Error(error.message); + if (err) { + console.error('Error creating donation:', err); + return { success: false, error: err }; } - - return inserted; // return the row that was created + return { success: true, data: entry }; // return the row that was created } // deletes donation based on donation_id export async function deleteDonation(donationId: string) { const supabase = await createClient(); - const { error } = await supabase + const { error: err } = await supabase .from('donations') .delete() .eq('donation_id', donationId); - if (error) { - console.error('Error deleting donation:', error); - throw new Error(error.message); + if (err) { + console.error('Error deleting donation:', err); + return { success: false, error: err }; } - return { success: true }; } diff --git a/app/api/example/actions.ts b/app/api/example/actions.ts index 159ae62..3d5b37d 100644 --- a/app/api/example/actions.ts +++ b/app/api/example/actions.ts @@ -5,9 +5,15 @@ import { createClient } from '@/app/lib/supabase/server-client'; export const createExampleEntry = async (data: Example) => { const supabase = await createClient(); - const { error } = await supabase.from('example').insert(data); + const { data: entry, error: err } = await supabase + .from('example') + .insert(data) + .select() + .single(); - if (error) { - throw error; + if (err) { + console.error('Error creating example:', err); + return { success: false, error: err }; } + return { success: true, data: entry }; }; diff --git a/app/api/inventory-items/actions.ts b/app/api/inventory-items/actions.ts index 3a67cfa..0004462 100644 --- a/app/api/inventory-items/actions.ts +++ b/app/api/inventory-items/actions.ts @@ -5,36 +5,46 @@ import { createClient } from '@/app/lib/supabase/server-client'; export const createItem = async (data: InventoryItemInsert) => { const supabase = await createClient(); - const { error } = await supabase.from('inventory_items').insert(data); + const { data: entry, error: err } = await supabase + .from('inventory_items') + .insert(data) + .select() + .single(); - if (error) { - throw error; + if (err) { + console.error('Error creating inventory item:', err); + return { success: false, error: err }; } + return { success: true, data: entry }; }; export const changeItemQuantity = async ( - inventoryId: string, - updatedAvailableQuantity: number, + inventoryItemId: string, + newQuantity: number, ) => { const supabase = await createClient(); - const { error } = await supabase + const { error: err } = await supabase .from('inventory_items') - .update({ quantity_available: updatedAvailableQuantity }) - .eq('inventory_item_id', inventoryId); + .update({ quantity_available: newQuantity }) + .eq('inventory_item_id', inventoryItemId); - if (error) { - throw error; + if (err) { + console.error('Error changing inventory item quantity:', err); + return { success: false, error: err }; } + return { success: true }; }; -export const deleteItem = async (inventoryId: string) => { +export const deleteItem = async (inventoryItemId: string) => { const supabase = await createClient(); - const { error } = await supabase + const { error: err } = await supabase .from('inventory_items') .delete() - .eq('inventory_item_id', inventoryId); + .eq('inventory_item_id', inventoryItemId); - if (error) { - throw error; + if (err) { + console.error('Error deleting inventory item:', err); + return { success: false, error: err }; } + return { success: true }; }; diff --git a/app/api/stores/actions.ts b/app/api/stores/actions.ts index 2aa6b59..0fb4718 100644 --- a/app/api/stores/actions.ts +++ b/app/api/stores/actions.ts @@ -6,21 +6,30 @@ import { StoreInsert } from '@/app/types/store'; // create store export const createStore = async (data: StoreInsert) => { const supabase = await createClient(); - const { error } = await supabase.from('stores').insert(data); - if (error) { - throw error; + const { data: entry, error: err } = await supabase + .from('stores') + .insert(data) + .select() + .single(); + + if (err) { + console.error('Error creating store:', err); + return { success: false, error: err }; } + return { success: true, data: entry }; }; -// action deleteStore // delete store given store_id export const deleteStore = async (storeId: string) => { const supabase = await createClient(); - const { error } = await supabase + const { error: err } = await supabase .from('stores') .delete() .eq('store_id', storeId); - if (error) { - throw error; + + if (err) { + console.error('Error deleting store:', err); + return { success: false, error: err }; } + return { success: true }; }; diff --git a/app/api/ticket-items/actions.ts b/app/api/ticket-items/actions.ts index a2365e1..4404e09 100644 --- a/app/api/ticket-items/actions.ts +++ b/app/api/ticket-items/actions.ts @@ -3,30 +3,37 @@ import { TicketItemInsert } from '@/app/types/ticket'; import { createClient } from '@/app/lib/supabase/server-client'; -export async function createTicketItem(ticketItemData: TicketItemInsert) { +export async function createTicketItem(data: TicketItemInsert) { const supabase = await createClient(); // Server Action uses a mutation method - const { error } = await supabase.from('ticket_items').insert(ticketItemData); - //.select().single(); <- kept only for debugging to see inserted entry - if (error) { - console.error('Error inserting ticket item:', error); - throw new Error(error.message); + const { data: entry, error: err } = await supabase + .from('ticket_items') + .insert(data) + .select() + .single(); + + if (err) { + console.error('Error creating ticket item:', err); + return { success: false, error: err }; } + return { success: true, data: entry }; } export async function changeTicketItemQuantity( ticketItemId: string, - newQuantity: number | null, + newQuantity: number, ) { const supabase = await createClient(); - const { error } = await supabase + const { error: err } = await supabase .from('ticket_items') .update({ quantity_requested: newQuantity }) .eq('ticket_item_id', ticketItemId); - if (error) { - console.log('Error updating ticket item:', error); - throw new Error(error.message); + + if (err) { + console.error('Error changing ticket item quantity:', err); + return { success: false, error: err }; } + return { success: true }; } export async function changeTicketItemDescription( @@ -34,24 +41,28 @@ export async function changeTicketItemDescription( newDescription: string, ) { const supabase = await createClient(); - const { error } = await supabase + const { error: err } = await supabase .from('ticket_items') .update({ free_text_description: newDescription }) .eq('ticket_item_id', ticketItemId); - if (error) { - console.log('Error updating ticket free text description:', error); - throw new Error(error.message); + + if (err) { + console.error('Error changing ticket item description:', err); + return { success: false, error: err }; } + return { success: true }; } export async function deleteTicketItem(ticketItemId: string) { const supabase = await createClient(); - const { error } = await supabase + const { error: err } = await supabase .from('ticket_items') .delete() .eq('ticket_item_id', ticketItemId); - if (error) { - console.log('Error deleting ticket:', error); - throw new Error(error.message); + + if (err) { + console.error('Error deleting ticket item:', err); + return { success: false, error: err }; } + return { success: true }; } diff --git a/app/api/tickets/actions.ts b/app/api/tickets/actions.ts index 198b3cc..cdb96f8 100644 --- a/app/api/tickets/actions.ts +++ b/app/api/tickets/actions.ts @@ -3,15 +3,20 @@ import { TicketInsert } from '@/app/types/ticket'; import { createClient } from '@/app/lib/supabase/server-client'; -export async function createTicket(formData: TicketInsert) { +export async function createTicket(data: TicketInsert) { const supabase = await createClient(); // pulling out the error from the insert into tickets table - const { error: err } = await supabase.from('tickets').insert(formData); + const { data: entry, error: err } = await supabase + .from('tickets') + .insert(data) + .select() + .single(); if (err) { + console.error('Error creating ticket:', err); return { success: false, error: err }; } - return { success: true }; + return { success: true, data: entry }; } export async function deleteTicket(ticketId: string) { @@ -20,7 +25,9 @@ export async function deleteTicket(ticketId: string) { .from('tickets') .delete() .eq('ticket_id', ticketId); + if (err) { + console.error('Error deleting ticket:', err); return { success: false, error: err }; } return { success: true }; @@ -32,7 +39,9 @@ export async function updateTicketStatus(newStatus: string, ticketId: string) { .from('tickets') .update({ status: newStatus }) .eq('ticket_id', ticketId); + if (err) { + console.error('Error changing ticket status:', err); return { success: false, error: err }; } return { success: true }; From 1b3d3d5d41557b5c5354d0698adbec74842ed7ff Mon Sep 17 00:00:00 2001 From: joy-y-cheng Date: Wed, 17 Dec 2025 20:33:08 -0800 Subject: [PATCH 40/40] Minor fixes to tables and types --- app/components/test/InventoryItemsTestComponent.tsx | 2 +- app/types/donation.ts | 8 ++++---- app/types/inventory.ts | 6 +++--- .../migrations/20251218042006_minor_table_edits.sql | 11 +++++++++++ supabase/schemas/donations.sql | 2 +- supabase/schemas/inventory_items.sql | 2 +- supabase/schemas/stores.sql | 4 ++-- 7 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 supabase/migrations/20251218042006_minor_table_edits.sql diff --git a/app/components/test/InventoryItemsTestComponent.tsx b/app/components/test/InventoryItemsTestComponent.tsx index c5915a2..9cc69ad 100644 --- a/app/components/test/InventoryItemsTestComponent.tsx +++ b/app/components/test/InventoryItemsTestComponent.tsx @@ -15,7 +15,7 @@ export default function InventoryItemsTestComponent() { const data: InventoryItemInsert = { store_id: '4c7b2b65-16df-44b5-a3c2-e2fcd090b76c', - subcategory: 1, + subcategory_id: 1, item: 'Sample Item', description: 'test description', photo_url: 'http://example.com/photo.jpg', diff --git a/app/types/donation.ts b/app/types/donation.ts index a9a2764..8909d9e 100644 --- a/app/types/donation.ts +++ b/app/types/donation.ts @@ -1,19 +1,19 @@ export type Donation = { - donation_id?: string; // optional, generated by database + donation_id: string; // generated by database receiver_user_id: string; store_id?: string | null; - date_submitted?: Date | string; // generated by default + date_submitted: Date | string; // generated by default donor_is_individual: boolean; donor_individual_name?: string | null; // optional donor_business_name?: string | null; // optional donor_business_contact_name?: string | null; // optional - donor_email?: string | null; // optional + donor_email: string; donor_phone?: string | null; // optional - donor_street_address: string | null; // optional + donor_street_address?: string | null; // optional donor_receive_mailings: boolean; donor_receive_emails: boolean; donor_remain_anonymous: boolean; diff --git a/app/types/inventory.ts b/app/types/inventory.ts index 47fe9ea..adf17ad 100644 --- a/app/types/inventory.ts +++ b/app/types/inventory.ts @@ -1,10 +1,10 @@ export type InventoryItem = { - inventory_item_id?: string; // UUID + inventory_item_id: string; // UUID store_id: string; // UUID - subcategory: number; + subcategory_id: number; item: string; description: string; - photo_url: string; + photo_url?: string | null; quantity_available: number; is_hidden: boolean; }; diff --git a/supabase/migrations/20251218042006_minor_table_edits.sql b/supabase/migrations/20251218042006_minor_table_edits.sql new file mode 100644 index 0000000..79aca1f --- /dev/null +++ b/supabase/migrations/20251218042006_minor_table_edits.sql @@ -0,0 +1,11 @@ +alter table "public"."donations" alter column "donor_email" set not null; + +alter table "public"."inventory_items" drop column "subcategory"; + +alter table "public"."inventory_items" add column "subcategory_id" integer not null; + +alter table "public"."stores" alter column "name" set not null; + +alter table "public"."stores" alter column "street_address" set not null; + + diff --git a/supabase/schemas/donations.sql b/supabase/schemas/donations.sql index 1a3612f..b3f9916 100644 --- a/supabase/schemas/donations.sql +++ b/supabase/schemas/donations.sql @@ -12,7 +12,7 @@ create table donations ( donor_individual_name text, -- optional, used if donor is an individual donor_business_name text, -- optional, used if donor is a business donor_business_contact_name text, -- optional, used if donor is a business - donor_email varchar(255), -- optional, can't store more than 255 characters + donor_email varchar(255) not null, -- can't store more than 255 characters donor_phone varchar(20), -- optional, can't store more than 20 characters donor_street_address text, -- optional, street address donor_receive_mailings boolean not null, -- required, whether or not donor wants to receive mailings diff --git a/supabase/schemas/inventory_items.sql b/supabase/schemas/inventory_items.sql index 5c3d158..577a71a 100644 --- a/supabase/schemas/inventory_items.sql +++ b/supabase/schemas/inventory_items.sql @@ -1,7 +1,7 @@ create table "inventory_items" ( "inventory_item_id" uuid primary key default uuid_generate_v4 (), "store_id" uuid not null, - "subcategory" int not null, + "subcategory_id" int not null, "item" varchar(255) not null, "description" text not null, "photo_url" text, diff --git a/supabase/schemas/stores.sql b/supabase/schemas/stores.sql index 063c91f..5dae477 100644 --- a/supabase/schemas/stores.sql +++ b/supabase/schemas/stores.sql @@ -1,7 +1,7 @@ create table "stores" ( store_id uuid default uuid_generate_v4 () primary key, - name text, - street_address text + name text not null, + street_address text not null ); alter table "stores" enable row level security;