diff --git a/app/components/feature/CpSponsorCard.vue b/app/components/shared/CpContentCard.vue
similarity index 67%
rename from app/components/feature/CpSponsorCard.vue
rename to app/components/shared/CpContentCard.vue
index 596f5b8..ef0b17a 100644
--- a/app/components/feature/CpSponsorCard.vue
+++ b/app/components/shared/CpContentCard.vue
@@ -1,20 +1,37 @@
@@ -26,19 +43,19 @@ const ribbonTypeKey = computed(() =>
:class="ribbonColorClass"
>
{{ t(`ribbon.${ribbonTypeKey}`) }}
- {{ t('ribbon.years', { n: sponsor.reward_data }) }}
+ {{ t('ribbon.years', { n: item.reward_data }) }}
@@ -49,31 +66,31 @@ const ribbonTypeKey = computed(() =>
external
rel="noreferrer"
target="_blank"
- :to="sponsor.link"
+ :to="item.link"
>
- {{ sponsor.name[locale] }}
+ {{ item.title[locale] }}
@@ -82,7 +99,7 @@ const ribbonTypeKey = computed(() =>
diff --git a/app/pages/fringe.vue b/app/pages/fringe.vue
new file mode 100644
index 0000000..3a484de
--- /dev/null
+++ b/app/pages/fringe.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ {{ t('noFringe') }}
+
+
+
+
+
+
+
+zh:
+ noFringe: "周邊活動目前尚未公布。"
+en:
+ noFringe: "Fringe events have not been announced yet."
+
diff --git a/app/pages/sponsor.vue b/app/pages/sponsor.vue
index db022b0..cab43ef 100644
--- a/app/pages/sponsor.vue
+++ b/app/pages/sponsor.vue
@@ -2,7 +2,7 @@
import type { Sponsor } from '#shared/types/sponsor'
import { useI18n } from 'vue-i18n'
import { SPONSOR_LEVELS } from '#shared/types/sponsor'
-import SponsorCard from '~/components/feature/CpSponsorCard.vue'
+import CpContentCard from '~/components/shared/CpContentCard.vue'
const { t } = useI18n()
const localePath = useLocalePath()
@@ -80,7 +80,17 @@ useSeoMeta({
v-for="sponsor in sponsors"
:key="sponsor.id"
>
-
+
diff --git a/server/api/fringe.get.ts b/server/api/fringe.get.ts
new file mode 100644
index 0000000..7c67672
--- /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 bd3493e..78d5653 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 0000000..19c8997
--- /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 0000000..46a8d47
--- /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 ddc0839..706aae1 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)