Skip to content

Commit 3ff0b52

Browse files
committed
feat: implement registration page with digital wallet description
1 parent 8b4de75 commit 3ff0b52

7 files changed

Lines changed: 166 additions & 64 deletions

File tree

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,8 @@ pnpm-debug.log*
2323
static
2424

2525
src/data/schedule.json
26-
public/data/schedule.json
26+
public/data/schedule.json
27+
28+
.claude/
29+
.codex/
30+
.gemini/

src/components/Navbar.astro

Lines changed: 4 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { pathnameWithoutBaseAndLocale } from '~/utils/build/pathnameWithoutBaseA
44
import { getRelativeLocaleUrl } from 'astro:i18n'
55
import Icon from '~/components/Icon.astro'
66
import Link from '~/components/Link.astro'
7-
import OpassDownload from '~/components/OpassDownload.astro'
87
98
const { t } = useI18n(Astro.url.pathname)
109
const pathname = pathnameWithoutBaseAndLocale(Astro.url.pathname)
@@ -181,18 +180,18 @@ const navs: NavItem[] = [
181180
<a href={getRelativeLocaleUrl('en', pathname)} class="text-lg">English</a>
182181
</div>
183182

184-
<a class="register-btn btn-brand flex items-center justify-between px-4 py-3 text-lg" href="#">
183+
<Link class="mobile-nav-item btn-brand flex items-center justify-between px-4 py-3 text-lg" href="register">
185184
{t('nav.register')}
186185
<Icon name="mdi:arrow-right" width="28" />
187-
</a>
186+
</Link>
188187
</div>
189188
</div>
190189
</div>
191190
</div>
192191

193-
<a class="register-btn btn-brand hidden h-full shrink-0 items-center justify-center px-8 md:flex" href="#">
192+
<Link class="btn-brand hidden h-full shrink-0 items-center justify-center px-8 md:flex" href="register">
194193
{t('nav.register')}
195-
</a>
194+
</Link>
196195

197196
<!-- ── Desktop submenu panels (auto-generated from navs data) ──────────────
198197
To add a submenu: add a `submenu: { columns: [...] }` entry to a NavItem.
@@ -233,53 +232,10 @@ const navs: NavItem[] = [
233232
))
234233
}
235234

236-
<template id="register-modal-header">
237-
<h1>{t('nav.register')}</h1>
238-
</template>
239-
<template id="register-modal-body">
240-
<div class="text-[#1e1e1e]">
241-
<div class="mb-8">
242-
<a href="https://summit.g0v.tw/tickets" target="_blank" rel="noopener noreferrer" class="btn btn-primary flex items-center gap-2 md:inline-flex">
243-
{t('register.cta')}<Icon name="mdi:external-link" width="22" />
244-
</a>
245-
</div>
246-
247-
<div class="mb-12 grid gap-8 md:grid-cols-2">
248-
<div class="flex flex-col">
249-
<h4 class="text-info-dark mb-2">{t('register.time')}</h4>
250-
<div class="mb-4">2026.05.23-24 | 09:00-17:30</div>
251-
<div class="mt-auto">
252-
<a
253-
href="https://calendar.google.com/calendar/render?action=TEMPLATE&text=g0v+Summit+2026&dates=20260523T010000Z/20260524T093000Z&details=g0v+Summit+2026&location=Academia+Sinica"
254-
target="_blank"
255-
class="btn btn-primary flex items-center gap-2 md:inline-flex"
256-
>
257-
{t('register.add_to_calendar')}<Icon name="mdi:external-link" width="22" />
258-
</a>
259-
</div>
260-
</div>
261-
<div class="flex flex-col">
262-
<h4 class="text-info-dark mb-2">{t('register.location')}</h4>
263-
<div class="mb-4">
264-
{t('register.location_value_1')}<br />
265-
<span class="text-gray-700">{t('register.location_value_2')}</span>
266-
</div>
267-
<div class="mt-auto">
268-
<Link href="visit" class="btn btn-primary flex items-center gap-2 md:inline-flex">
269-
{t('register.transportation')}<Icon name="mdi:external-link" width="22" />
270-
</Link>
271-
</div>
272-
</div>
273-
</div>
274-
275-
<OpassDownload />
276-
</div>
277-
</template>
278235
</nav>
279236

280237
<script>
281238
import $ from 'jquery'
282-
import { showModalWith } from '~/utils/client/modal'
283239

284240
// ── Mobile nav toggle ────────────────────────────────────────────────────────
285241
const $mobileNav = $('#mobile-nav')
@@ -293,17 +249,6 @@ const navs: NavItem[] = [
293249
$('#burger').toggleClass('is-active')
294250
}
295251

296-
// ── Register modal ───────────────────────────────────────────────────────────
297-
$('.register-btn').on('click', function (e) {
298-
e.preventDefault()
299-
if ($mobileNav.hasClass('grid-rows-[1fr]')) toggleMobileNav()
300-
showModalWith({
301-
header: $('#register-modal-header').html(),
302-
body: $('#register-modal-body').html(),
303-
size: 'lg',
304-
})
305-
})
306-
307252
// ── Submenu helpers ──────────────────────────────────────────────────────────
308253
function closeSubmenu($submenu: JQuery<HTMLElement>, $toggle: JQuery<HTMLElement>) {
309254
$submenu.removeClass('grid-rows-[1fr]')

src/components/QA.astro

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
import { useI18n } from '~/i18n/utils'
3+
const { t } = useI18n(Astro.url.pathname)
4+
const { qKey, aKey } = Astro.props
5+
---
6+
7+
<details class="qa group mb-3 rounded-md border border-neutral-50 bg-white p-4 shadow-sm">
8+
<summary class="flex cursor-pointer list-none items-center justify-between gap-4 font-semibold">
9+
<span>{t(qKey)}</span>
10+
<span class="arrow inline-block transition-transform duration-300 group-open:rotate-180">▾</span>
11+
</summary>
12+
<div class="content text-sm md:text-base">
13+
<p class="mt-3">{t(aKey)}</p>
14+
</div>
15+
</details>
16+
17+
<style>
18+
details.qa summary::-webkit-details-marker {
19+
display: none;
20+
}
21+
details.qa::details-content {
22+
max-height: 0;
23+
overflow: hidden;
24+
opacity: 0;
25+
transition:
26+
max-height 0.3s ease,
27+
opacity 0.3s ease,
28+
content-visibility 0.3s allow-discrete;
29+
}
30+
details.qa[open]::details-content {
31+
max-height: 999px;
32+
opacity: 1;
33+
}
34+
@starting-style {
35+
details.qa[open]::details-content {
36+
max-height: 0;
37+
opacity: 0;
38+
}
39+
}
40+
</style>

src/i18n/messages/en.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,16 @@
255255
"location_value_1": "Humanities & Social Sciences Building, Academia Sinica",
256256
"location_value_2": "128 Academia Rd. Sec. 2, Nangang District, Taipei, Taiwan",
257257
"add_to_calendar": "Add to Calendar",
258-
"transportation": "Transportation"
258+
"transportation": "Transportation",
259+
"qa": {
260+
"q1": "我的資料會被傳給數位發展部嗎?",
261+
"a1": "不會,發行與驗證伺服器的原始碼由數位發展部提供,並由 g0v Summit 志工於大會伺服器上架設並開發相關應用,發行證件所用的資料來自應援售票系統,證件資料僅會儲存於 App 內,報到資訊僅會留存於 g0v Summit 伺服器上並於大會後刪除。",
262+
"q2": "大會會索取我的哪些資料?",
263+
"a2": "證件上僅會有會眾已經於應援售票系統提供的資料,但不會全部放上。會放在證件上的與驗證過程索取的內容我們會遵從資料最小化的原則,只放上/索取必要資訊。例如透過數位憑證皮夾的選擇性揭露功能,報到時我們僅需索取能避免重複報到的欄位(如票號),而不需要證件上所有資料。在驗證資料送出前你可以在 App 上再度檢視我們索取的內容。若有其他應用場景,如領取便當,大會也僅需要知道你的餐飲資訊(葷/素),不需要索取額外的個資。",
264+
"q3": "為什麼你們不自行編譯 App 的原始碼讓會眾安裝或整合進 OPass?",
265+
"a3": "沒時間...QQ",
266+
"q4": "我一定要用數位憑證皮夾報到嗎?",
267+
"a4": "不用,我們理解不是每個人都有符合條件的手機能安裝,也可能有更嚴格的隱私考量,可以只以票號與報名資訊於現場進行報到。我們甚至提供現金購票與報到的選項。用現金購票者的資料完全不會經過應援或大會的線上系統,詳情請見官網與行前信!"
268+
}
259269
}
260-
}
270+
}

src/i18n/messages/zh-tw.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,16 @@
255255
"location_value_1": "中央研究院 人文社會科學館",
256256
"location_value_2": "臺北市南港區研究院路二段 128 號",
257257
"add_to_calendar": "加入行事曆",
258-
"transportation": "交通方式"
258+
"transportation": "交通方式",
259+
"qa": {
260+
"q1": "我的資料會被傳給數位發展部嗎?",
261+
"a1": "不會,發行與驗證伺服器的原始碼由數位發展部提供,並由 g0v Summit 志工於大會伺服器上架設並開發相關應用,發行證件所用的資料來自應援售票系統,證件資料僅會儲存於 App 內,報到資訊僅會留存於 g0v Summit 伺服器上並於大會後刪除。",
262+
"q2": "大會會索取我的哪些資料?",
263+
"a2": "證件上僅會有會眾已經於應援售票系統提供的資料,但不會全部放上。會放在證件上的與驗證過程索取的內容我們會遵從資料最小化的原則,只放上/索取必要資訊。例如透過數位憑證皮夾的選擇性揭露功能,報到時我們僅需索取能避免重複報到的欄位(如票號),而不需要證件上所有資料。在驗證資料送出前你可以在 App 上再度檢視我們索取的內容。若有其他應用場景,如領取便當,大會也僅需要知道你的餐飲資訊(葷/素),不需要索取額外的個資。",
264+
"q3": "為什麼你們不自行編譯 App 的原始碼讓會眾安裝或整合進 OPass?",
265+
"a3": "沒時間...QQ",
266+
"q4": "我一定要用數位憑證皮夾報到嗎?",
267+
"a4": "不用,我們理解不是每個人都有符合條件的手機能安裝,也可能有更嚴格的隱私考量,可以只以票號與報名資訊於現場進行報到。我們甚至提供現金購票與報到的選項。用現金購票者的資料完全不會經過應援或大會的線上系統,詳情請見官網與行前信!"
268+
}
259269
}
260-
}
270+
}

src/pages/en/register.astro

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
import Page from '~/pages/register.astro'
3+
---
4+
5+
<Page />

src/pages/register.astro

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
import Layout from '~/layouts/Layout.astro'
3+
import Link from '~/components/Link.astro'
4+
import Icon from '~/components/Icon.astro'
5+
import QA from '~/components/QA.astro'
6+
import OpassDownload from '~/components/OpassDownload.astro'
7+
import { useI18n } from '~/i18n/utils'
8+
9+
const { t } = useI18n(Astro.url.pathname)
10+
---
11+
12+
<Layout pageName={t('nav.register')}>
13+
<main class="container py-8 md:py-12">
14+
<h1 class="mb-8">{t('nav.register')}</h1>
15+
16+
<div class="mb-8">
17+
<a href="https://summit.g0v.tw/tickets" target="_blank" rel="noopener noreferrer" class="btn btn-primary flex items-center gap-2 md:inline-flex">
18+
{t('register.cta')}<Icon name="mdi:external-link" width="22" />
19+
</a>
20+
</div>
21+
22+
<div class="mb-12 grid gap-8 md:grid-cols-2">
23+
<div class="flex flex-col">
24+
<h4 class="text-info-dark mb-2">{t('register.time')}</h4>
25+
<div class="mb-4">2026.05.23-24 | 09:00-17:30</div>
26+
<div class="mt-auto">
27+
<a
28+
href="https://calendar.google.com/calendar/render?action=TEMPLATE&text=g0v+Summit+2026&dates=20260523T010000Z/20260524T093000Z&details=g0v+Summit+2026&location=Academia+Sinica"
29+
target="_blank"
30+
class="btn btn-primary flex items-center gap-2 md:inline-flex"
31+
>
32+
{t('register.add_to_calendar')}<Icon name="mdi:external-link" width="22" />
33+
</a>
34+
</div>
35+
</div>
36+
<div class="flex flex-col">
37+
<h4 class="text-info-dark mb-2">{t('register.location')}</h4>
38+
<div class="mb-4">
39+
{t('register.location_value_1')}<br />
40+
<span class="text-gray-700">{t('register.location_value_2')}</span>
41+
</div>
42+
<div class="mt-auto">
43+
<Link href="visit" class="btn btn-primary flex items-center gap-2 md:inline-flex">
44+
{t('register.transportation')}<Icon name="mdi:external-link" width="22" />
45+
</Link>
46+
</div>
47+
</div>
48+
</div>
49+
<hr />
50+
<div class="my-12">
51+
<h2 class="mb-8">如何報到</h2>
52+
<div class="mb-8">
53+
<p>
54+
你聽過數位憑證皮夾嗎?本次 g0v Summit 與數位發展部合作,採用<a href="https://wallet.gov.tw/zh-tw" target="_blank">數位憑證皮夾</a
55+
>作為主要報到管道。數位憑證皮夾是臺灣少數政府機構在開發前就將開放原始碼作為標案目標、從伺服器到手機 App 都開源的專案。這屆 g0v Summit 的志工們認同 Public Money, Public Code
56+
的精神,期望能共同探索一個可被檢視與信任、可選擇性揭露、保護隱私的身份驗證流程,歡迎各位來一起參與這次嘗試!
57+
</p>
58+
<h3 class="my-8">數位皮夾報到流程(需要一定版本以上的 Android 或 iOS 手機):</h3>
59+
<ol>
60+
<li>於 Google Play Store 或 iOS App Store 下載數位發展部的<a href="https://wallet.gov.tw/zh-tw" target="_blank">「數位憑證皮夾」App</a>並完成初始設定</li>
61+
<li>大會行前信寄出時會同時寄給各會眾帶有必要資訊的票證 QR Code 連結,使用數位憑證皮夾 App 掃描或開啟連結即可將票證加入皮夾中</li>
62+
<li>大會報到現場提供報到用驗證 QR Code,使用 App 掃描可以預覽大會向你索取的證件資訊</li>
63+
<li>確認並送出即可完成報到</li>
64+
</ol>
65+
<h3 class="my-8">到底什麼是數位憑證皮夾?為什麼要用?</h3>
66+
<p>
67+
在世界各地越加傾向管控網路身份的現在,如何在網路世界做基本的身份驗證又保護隱私,是各國政府跟民間都傷透腦筋的問題。如果由政府主導,有大量蒐集、監控人民資訊的隱憂;若沒有規範,可能造成各大企業在仍有驗證身份的需求下,各自開發更加容易侵犯隱私的追蹤方式。
68+
</p>
69+
<p>
70+
為了找到共識,OpenID Foundation、W3C、IETF 都有工作小組進行了長久的討論。以身分自主(Self-sovereign Identity, SSI)的理想與去中心化識別(Decentralized Identifiers,
71+
DIDs)的各式技術為基礎,最終提出了可驗證憑證(Verifiable Credentials, VC)的共識。
72+
</p>
73+
<p>
74+
本次數位發展部主導開發的數位憑證皮夾便是基於 OpenID4VC 標準所開發,建立發行端——驗證端——持有端的三方架構,並皆開放原始碼,因此發行、驗證伺服器與保存證件的 App
75+
皆可由民間自行架設,無需經過中央管理的資料庫。我們也期望藉由這次嘗試,檢查專案是否符合國際標準,並期望能與其他國際上的實作(如歐盟的 EUDI Wallet)接軌。
76+
</p>
77+
</div>
78+
</div>
79+
<div class="mt-12 mb-20">
80+
{
81+
[1, 2, 3, 4].map(i => {
82+
return <QA qKey={`register.qa.q${i}`} aKey={`register.qa.a${i}`} />
83+
})
84+
}
85+
</div>
86+
<OpassDownload />
87+
</main>
88+
</Layout>

0 commit comments

Comments
 (0)