From beba0425b1a5faaebd5ef43e19c460c475488c63 Mon Sep 17 00:00:00 2001 From: Link1515 Date: Tue, 2 Jun 2026 09:53:48 +0800 Subject: [PATCH 1/2] feat: add fringe events page and API --- app/components/feature/CpFringeCard.vue | 20 ++++ app/components/feature/CpSponsorCard.vue | 106 ++----------------- app/components/shared/CpContentCard.vue | 123 +++++++++++++++++++++++ app/pages/fringe.vue | 37 +++++++ server/api/fringe.get.ts | 16 +++ server/api/sponsor.get.ts | 14 +-- server/utils/images/index.ts | 9 ++ shared/types/fringe.ts | 30 ++++++ shared/types/sheets.ts | 5 +- 9 files changed, 252 insertions(+), 108 deletions(-) create mode 100644 app/components/feature/CpFringeCard.vue create mode 100644 app/components/shared/CpContentCard.vue create mode 100644 app/pages/fringe.vue create mode 100644 server/api/fringe.get.ts create mode 100644 server/utils/images/index.ts create mode 100644 shared/types/fringe.ts diff --git a/app/components/feature/CpFringeCard.vue b/app/components/feature/CpFringeCard.vue new file mode 100644 index 00000000..fa9cc718 --- /dev/null +++ b/app/components/feature/CpFringeCard.vue @@ -0,0 +1,20 @@ + + + diff --git a/app/components/feature/CpSponsorCard.vue b/app/components/feature/CpSponsorCard.vue index 596f5b8f..d11dd7d2 100644 --- a/app/components/feature/CpSponsorCard.vue +++ b/app/components/feature/CpSponsorCard.vue @@ -1,106 +1,22 @@ - - -zh: - show_less: "收合" - read_more: "更多" - ribbon: - consecutive: "連續贊助" - cumulative: "累積贊助" - years: "{n} 年" -en: - show_less: "Show less" - read_more: "Read more" - ribbon: - consecutive: "Consecutive" - cumulative: "Cumulative" - years: "{n} Yrs" - diff --git a/app/components/shared/CpContentCard.vue b/app/components/shared/CpContentCard.vue new file mode 100644 index 00000000..ef0b17a9 --- /dev/null +++ b/app/components/shared/CpContentCard.vue @@ -0,0 +1,123 @@ + + + + + +zh: + show_less: "收合" + read_more: "更多" + ribbon: + consecutive: "連續贊助" + cumulative: "累積贊助" + years: "{n} 年" +en: + show_less: "Show less" + read_more: "Read more" + ribbon: + consecutive: "Consecutive" + cumulative: "Cumulative" + years: "{n} Yrs" + diff --git a/app/pages/fringe.vue b/app/pages/fringe.vue new file mode 100644 index 00000000..e2509642 --- /dev/null +++ b/app/pages/fringe.vue @@ -0,0 +1,37 @@ + + + + + +zh: + noFringe: "周邊活動目前尚未公布。" +en: + noFringe: "Fringe events have not been announced yet." + diff --git a/server/api/fringe.get.ts b/server/api/fringe.get.ts new file mode 100644 index 00000000..7c67672b --- /dev/null +++ b/server/api/fringe.get.ts @@ -0,0 +1,16 @@ +import { transformGoogleDriveImageUrl } from '../utils/images' +import { fetchSheet } from '../utils/sheets' + +export default defineEventHandler(async () => { + const fringeRows = await fetchSheet('fringe') + + const fringes = import.meta.dev ? fringeRows : fringeRows.filter(({ publish }) => publish) + + return fringes.map(({ id, title_zh, title_en, intro_zh, intro_en, link, logo }) => ({ + id, + title: { zh: title_zh, en: title_en }, + intro: { zh: intro_zh, en: intro_en }, + link, + logo: transformGoogleDriveImageUrl(logo), + })) +}) diff --git a/server/api/sponsor.get.ts b/server/api/sponsor.get.ts index bd3493e2..78d56534 100644 --- a/server/api/sponsor.get.ts +++ b/server/api/sponsor.get.ts @@ -1,16 +1,6 @@ +import { transformGoogleDriveImageUrl } from '../utils/images' import { fetchSheet } from '../utils/sheets' -function transformImageUrl(source: string) { - if (source.startsWith('https://drive.google.com/file/d/')) { - const id = source.split('/')[5] - const url = `https://drive.google.com/thumbnail?id=${id}` - - return url - } - - return source -} - export default defineEventHandler(async () => { const sheets = await fetchSheet('sponsor-list') @@ -22,6 +12,6 @@ export default defineEventHandler(async () => { ...attr, name: { zh: name_zh, en: name_en }, intro: { zh: intro_zh, en: intro_en }, - image: transformImageUrl(image), + image: transformGoogleDriveImageUrl(image), })) }) diff --git a/server/utils/images/index.ts b/server/utils/images/index.ts new file mode 100644 index 00000000..19c8997f --- /dev/null +++ b/server/utils/images/index.ts @@ -0,0 +1,9 @@ +export function transformGoogleDriveImageUrl(source: string) { + if (source.startsWith('https://drive.google.com/file/d/')) { + const id = source.split('/')[5] + + return `https://drive.google.com/thumbnail?id=${id}` + } + + return source +} diff --git a/shared/types/fringe.ts b/shared/types/fringe.ts new file mode 100644 index 00000000..46a8d470 --- /dev/null +++ b/shared/types/fringe.ts @@ -0,0 +1,30 @@ +import { z } from 'zod' + +export const FringeRowSchema = z.object({ + id: z.string(), + title_zh: z.string(), + title_en: z.string(), + intro_zh: z.string(), + intro_en: z.string(), + link: z.string(), + contact: z.string(), + contact_email: z.string(), + logo: z.string(), + publish: z.string().transform((val) => val.trim().toLowerCase() === 'true'), +}) +export type FringeRow = z.infer + +export const FringeSchema = z.object({ + id: z.string(), + title: z.object({ + zh: z.string(), + en: z.string(), + }), + intro: z.object({ + zh: z.string(), + en: z.string(), + }), + link: z.string(), + logo: z.string(), +}) +export type Fringe = z.infer diff --git a/shared/types/sheets.ts b/shared/types/sheets.ts index ddc08390..706aae19 100644 --- a/shared/types/sheets.ts +++ b/shared/types/sheets.ts @@ -1,17 +1,19 @@ import type * as z from 'zod' +import { FringeRowSchema } from './fringe' import { SponsorListRowSchema } from './sponsor' import { SponsorshipAddOnSchema, SponsorshipTierSchema } from './sponsorship' import { StaffRowSchema } from './staff' export type SheetID = typeof SHEET_IDS[number] -export const SHEET_IDS = Object.freeze(['sponsorship-tiers', 'sponsorship-add-ons-zh', 'sponsorship-add-ons-en', 'sponsor-list', 'staff'] as const) +export const SHEET_IDS = Object.freeze(['sponsorship-tiers', 'sponsorship-add-ons-zh', 'sponsorship-add-ons-en', 'sponsor-list', 'staff', 'fringe'] as const) export const SHEET_NAMES = Object.freeze({ 'sponsorship-tiers': '贊助方案', 'sponsorship-add-ons-zh': '贊助方案加價購(中文)', 'sponsorship-add-ons-en': '贊助方案加價購(英文)', 'sponsor-list': '贊助列表', 'staff': '工作夥伴', + 'fringe': '周邊資訊', } satisfies Record) export const SHEET_SCHEMAS = Object.freeze({ 'sponsorship-tiers': SponsorshipTierSchema, @@ -19,4 +21,5 @@ export const SHEET_SCHEMAS = Object.freeze({ 'sponsorship-add-ons-en': SponsorshipAddOnSchema, 'sponsor-list': SponsorListRowSchema, 'staff': StaffRowSchema, + 'fringe': FringeRowSchema, } satisfies Record) From db143f3f6dff8236b4af4100f7d505e5a8a7eb68 Mon Sep 17 00:00:00 2001 From: Link1515 Date: Thu, 4 Jun 2026 15:35:44 +0800 Subject: [PATCH 2/2] refactor: use shared content cards for sponsors and fringes --- app/components/feature/CpFringeCard.vue | 20 -------------------- app/components/feature/CpSponsorCard.vue | 22 ---------------------- app/pages/fringe.vue | 12 +++++++++--- app/pages/sponsor.vue | 14 ++++++++++++-- 4 files changed, 21 insertions(+), 47 deletions(-) delete mode 100644 app/components/feature/CpFringeCard.vue delete mode 100644 app/components/feature/CpSponsorCard.vue diff --git a/app/components/feature/CpFringeCard.vue b/app/components/feature/CpFringeCard.vue deleted file mode 100644 index fa9cc718..00000000 --- a/app/components/feature/CpFringeCard.vue +++ /dev/null @@ -1,20 +0,0 @@ - - - diff --git a/app/components/feature/CpSponsorCard.vue b/app/components/feature/CpSponsorCard.vue deleted file mode 100644 index d11dd7d2..00000000 --- a/app/components/feature/CpSponsorCard.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - diff --git a/app/pages/fringe.vue b/app/pages/fringe.vue index e2509642..3a484de2 100644 --- a/app/pages/fringe.vue +++ b/app/pages/fringe.vue @@ -1,7 +1,7 @@