diff --git a/src/lib/repository/programRepository.js b/src/lib/repository/programRepository.js new file mode 100644 index 0000000..bdceb9a --- /dev/null +++ b/src/lib/repository/programRepository.js @@ -0,0 +1,51 @@ +import { supabase } from "../supabaseClient"; + +class ProgramRepository { + async fetchTotalProcutCount() { + const { count, error } = await supabase.from("program").select("*", { count: "exact" }); + + if (error) throw new Error(error.message); + return count; + } + + async fetchProgramById({ programId }) { + if (programId === "undefined") return { program: {} }; + + const { data: program, error } = await supabase + .from("program") + .select("*, product(*), act(*)") + .eq("id", programId) + .order("createdAt", { + foreignTable: "act", + ascending: true, + }) + .maybeSingle(); + + if (error) throw new Error(error.message); + + return { program }; + } + + async fetchProgramByPaging({ pageNum = 1 }) { + let page = (Number(pageNum) - 1) * 20; + + if (page < 0) page = 0; + + const { data: programs, error } = await supabase + .from("program") + .select("*") + .order("createdAt", { ascending: true }) + .range(page, page + 19); + + if (error) throw new Error(error.message); + + return { programs }; + } + + async deleteProgramById({ programId }) { + const { error } = await supabase.from("program").delete().in("id", programId); + if (error) throw new Error(error.message); + } +} + +export default new ProgramRepository(); diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 0b48d77..507f33b 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -30,6 +30,9 @@ 상품 + 프로그램 쿠폰 diff --git a/src/routes/program/+page.server.js b/src/routes/program/+page.server.js new file mode 100644 index 0000000..92ff9e7 --- /dev/null +++ b/src/routes/program/+page.server.js @@ -0,0 +1,10 @@ +import programRepository from "../../lib/repository/programRepository.js"; + +export const load = async ({ url }) => { + const pageNum = url.searchParams.get("pageNum") || 1; + + const { programs } = await programRepository.fetchProgramByPaging({ pageNum }); + const totalProgramCount = await programRepository.fetchTotalProcutCount(); + + return { programs, totalProgramCount }; +}; diff --git a/src/routes/program/+page.svelte b/src/routes/program/+page.svelte new file mode 100644 index 0000000..8897c6b --- /dev/null +++ b/src/routes/program/+page.svelte @@ -0,0 +1,152 @@ + + +
+ + + + + + + + + + + + {#each programs as program, idx} + + + + + + + + {/each} + +
+
+ + +
+
Index Image Name InfoText
+
+ toggleProgramSelection(program)} + checked={selectedPrograms.some((selectedProgram) => selectedProgram.id === program.id)} + type="checkbox" + class="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-blue-600 dark:focus:ring-offset-gray-800" /> + +
+
{idx + 1} goto(`/program/${program.id}`)} + class="flex items-center whitespace-nowrap py-4 text-gray-900 dark:text-white"> + product + goto(`/program/${program.id}`)} class="px-6 py-4"> {program.name} goto(`/program/${program.id}`)} class="px-6 py-4"> +
+ {program.infoText} +
+
+ + {#if totalProgramCount > 0 && !isNaN(Number(pageNum))} +
+ + {#if Number(pageNum) !== 1} + + {/if} + + {#if Number(pageNum) < Math.ceil(totalProgramCount / 20)} + + {/if} + +
+ {/if} + +
+ +
+ +
+
+
diff --git a/src/routes/program/+server.js b/src/routes/program/+server.js new file mode 100644 index 0000000..9f41274 --- /dev/null +++ b/src/routes/program/+server.js @@ -0,0 +1,17 @@ +import { json } from "@sveltejs/kit"; +import programRepository from "../../lib/repository/programRepository.js"; + +export const DELETE = async ({ request }) => { + try { + const programs = await request.json(); + + const programIds = programs.map((err) => err.id); + + await programRepository.deleteProgramById({ programId: programIds }); + + return json(true); + } catch (err) { + console.error("product/server.js Error : ", err); + return json(false); + } +}; diff --git a/src/routes/program/[programId]/+page.server.js b/src/routes/program/[programId]/+page.server.js new file mode 100644 index 0000000..633bc57 --- /dev/null +++ b/src/routes/program/[programId]/+page.server.js @@ -0,0 +1,105 @@ +import programRepository from "../../../lib/repository/programRepository.js"; +import { supabase } from "../../../lib/supabaseClient.js"; + +export const load = async ({ params }) => { + const programId = params.programId; + + if (programId === "null") { + const program = {}; + return { program }; + } + + const { program } = await programRepository.fetchProgramById({ programId }); + + return { program }; +}; + +export const actions = { + save: async ({ request, params }) => { + const product = await request.formData(); + + // 기본 정보. + const basicInfo = { + name: product.get("name"), + isComingSoon: product.get("isComingSoon"), + duration: product.get("duration"), + feature: product.get("feature"), + infoText: product.get("infoText"), + ingredient: product.get("ingredient"), + manual: product.get("manual"), + }; + + if (product.get("bannerImage") === "no") basicInfo.bannerImageUrl = null; + else if (product.get("bannerImage") === "url") { + basicInfo.bannerImageUrl = product.get("bannerImageUrl"); + } else { + const file = product.get("bannerImageFile"); + // 파일 등록 후 url. 등록. + const { data, error } = await supabase.storage + .from("program-images") + .upload(`img/${params.programId}/banner/${file.name}`, file, { + cacheControl: "3600", + upsert: true, + }); + + let { data: imageUrl } = await supabase.storage.from("program-images").getPublicUrl(data.path); + imageUrl = imageUrl.publicUrl; + basicInfo.bannerImageUrl = imageUrl; + } + + if (product.get("productImage") === "no") basicInfo.productImageUrl = null; + else if (product.get("productImage") === "url") { + basicInfo.productImageUrl = product.get("productImageUrl"); + } else { + const file = product.get("productImageFile"); + const { data, error } = await supabase.storage + .from("program-images") + .upload(`img/${params.programId}/product/${file.name}`, file, { + cacheControl: "3600", + upsert: true, + }); + let { data: imageUrl } = await supabase.storage.from("program-images").getPublicUrl(data.path); + imageUrl = imageUrl.publicUrl; + basicInfo.productImageUrl = imageUrl; + } + + if (product.get("bannerComingSoonImage") === "no") basicInfo.bannerImageUrlComingSoon = null; + else if (product.get("bannerComingSoonImage") == "url") { + basicInfo.bannerImageUrlComingSoon = product.get("bannerComingSoonImageUrl"); + } else { + const file = product.get("bannerComingSoonImageFile"); + const { data, error } = await supabase.storage + .from("program-images") + .upload(`img/${params.programId}/bannerComingSoon/${file.name}`, file, { + cacheControl: "3600", + upsert: true, + }); + + let { data: imageUrl } = await supabase.storage.from("program-images").getPublicUrl(data.path); + imageUrl = imageUrl.publicUrl; + basicInfo.bannerImageUrlComingSoon = imageUrl; + } + + if (params.programId === "null") { + const { error } = await supabase.from("program").insert({ ...basicInfo }); + + if (error) { + console.error("Program Save(Insert) Err", error.message); + return "Fail"; + } + + return "Success"; + } + const { error } = await supabase + .from("program") + .update({ ...basicInfo }) + .eq("id", params.programId); + + if (error) { + console.error("Program Save Err", error.message); + return "Fail"; + } + + return "Success"; + }, +}; diff --git a/src/routes/program/[programId]/+page.svelte b/src/routes/program/[programId]/+page.svelte new file mode 100644 index 0000000..e787252 --- /dev/null +++ b/src/routes/program/[programId]/+page.svelte @@ -0,0 +1,238 @@ + + +
+
+
+
+

프로그램명

+ +
+ +
+

Is Coming Soon

+ +
+ +
+

프로그램 소요시간 (분)

+ +
+ +
+

Feature

+