Merge pull request 'fix(ui): stabilize theme colors and calendar end time' (#4) from fix/theme-contrast-and-calendar-end into master
Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
@@ -52,7 +52,7 @@ RSVP is a party: one or more guest names attached to a single RSVP + optional sh
|
|||||||
| Method | Path | Body | Notes |
|
| Method | Path | Body | Notes |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| POST | `/api/rsvp` | `{names: string[], message?}` | at least one non-blank name required; insert party + guests, email couple |
|
| POST | `/api/rsvp` | `{names: string[], message?}` | at least one non-blank name required; insert party + guests, email couple |
|
||||||
| GET | `/api/config` | — | `{weddingDate, ceremonyTime, rsvpByDate, venueName, venueAddress, rsvpPhone}` (all RFC3339 where dates) — single source of truth for countdowns, gate, and displayed details |
|
| GET | `/api/config` | — | `{weddingDate, eventEnd, ceremonyTime, rsvpByDate, venueName, venueAddress, rsvpPhone}` (all RFC3339 where dates) — single source of truth for countdowns, calendar, gate, and displayed details |
|
||||||
| POST | `/api/photos/upload` | multipart: `name, message?, email?, phone?, files[]` | rejected with 403 if `now < weddingDate`, **enforced server-side** regardless of client clock |
|
| POST | `/api/photos/upload` | multipart: `name, message?, email?, phone?, files[]` | rejected with 403 if `now < weddingDate`, **enforced server-side** regardless of client clock |
|
||||||
| GET | `/healthz` | — | liveness |
|
| GET | `/healthz` | — | liveness |
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ docker-compose.yml wedding app, published to 127.0.0.1:8080 only
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Config
|
## Config
|
||||||
Copy `config.example.yaml` to `config.yaml` and fill in the wedding date, SMTP settings, storage path, and (if enabled) the Origin service key. Path is resolved from `CONFIG_PATH` (default `./config.yaml`) — that's the only env var; everything else lives in the file.
|
Copy `config.example.yaml` to `config.yaml` and fill in the wedding date, event end time, SMTP settings, storage path, and (if enabled) the Origin service key. `wedding.event_end` is the explicit local RFC3339 end time used for calendar downloads. Path is resolved from `CONFIG_PATH` (default `./config.yaml`) — that's the only env var; everything else lives in the file.
|
||||||
|
|
||||||
When `origin.service_key` is configured, the server registers itself with Origin as the `generic` service type named `ZoeShaldon` immediately at startup and repeats the check-in every 24 hours. Registration failures are logged without taking down the wedding site. Leave the key empty to disable registration.
|
When `origin.service_key` is configured, the server registers itself with Origin as the `generic` service type named `ZoeShaldon` immediately at startup and repeats the check-in every 24 hours. Registration failures are logged without taking down the wedding site. Leave the key empty to disable registration.
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ site:
|
|||||||
|
|
||||||
wedding:
|
wedding:
|
||||||
date: "2026-11-07T16:00:00+02:00" # RFC3339, guest arrival time; drives countdown + photo upload gate
|
date: "2026-11-07T16:00:00+02:00" # RFC3339, guest arrival time; drives countdown + photo upload gate
|
||||||
|
event_end: "2026-11-07T23:30:00+02:00" # RFC3339, local end time used by calendar downloads
|
||||||
ceremony_time: "16:00 for 16:30" # display text
|
ceremony_time: "16:00 for 16:30" # display text
|
||||||
rsvp_by: "2026-10-01T08:00:00+02:00" # RFC3339, RSVP deadline; drives the RSVP countdown
|
rsvp_by: "2026-10-01T08:00:00+02:00" # RFC3339, RSVP deadline; drives the RSVP countdown
|
||||||
venue_name: "Rivier Plaas Wedding Venue"
|
venue_name: "Rivier Plaas Wedding Venue"
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
writeJSON(w, http.StatusOK, map[string]string{
|
writeJSON(w, http.StatusOK, map[string]string{
|
||||||
"siteUrl": s.cfg.Site.URL,
|
"siteUrl": s.cfg.Site.URL,
|
||||||
"weddingDate": s.cfg.Wedding.Date.Format(time.RFC3339),
|
"weddingDate": s.cfg.Wedding.Date.Format(time.RFC3339),
|
||||||
|
"eventEnd": s.cfg.Wedding.EventEnd.Format(time.RFC3339),
|
||||||
"ceremonyTime": s.cfg.Wedding.CeremonyTime,
|
"ceremonyTime": s.cfg.Wedding.CeremonyTime,
|
||||||
"rsvpByDate": s.cfg.Wedding.RSVPBy.Format(time.RFC3339),
|
"rsvpByDate": s.cfg.Wedding.RSVPBy.Format(time.RFC3339),
|
||||||
"venueName": s.cfg.Wedding.VenueName,
|
"venueName": s.cfg.Wedding.VenueName,
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ type SiteConfig struct {
|
|||||||
|
|
||||||
type WeddingConfig struct {
|
type WeddingConfig struct {
|
||||||
Date time.Time `yaml:"date"`
|
Date time.Time `yaml:"date"`
|
||||||
|
EventEnd time.Time `yaml:"event_end"`
|
||||||
CeremonyTime string `yaml:"ceremony_time"`
|
CeremonyTime string `yaml:"ceremony_time"`
|
||||||
RSVPBy time.Time `yaml:"rsvp_by"`
|
RSVPBy time.Time `yaml:"rsvp_by"`
|
||||||
VenueName string `yaml:"venue_name"`
|
VenueName string `yaml:"venue_name"`
|
||||||
@@ -112,6 +113,9 @@ func Load(path string) (*Config, error) {
|
|||||||
if cfg.Wedding.RSVPBy.IsZero() {
|
if cfg.Wedding.RSVPBy.IsZero() {
|
||||||
return nil, fmt.Errorf("wedding.rsvp_by is required in %s", path)
|
return nil, fmt.Errorf("wedding.rsvp_by is required in %s", path)
|
||||||
}
|
}
|
||||||
|
if cfg.Wedding.EventEnd.IsZero() {
|
||||||
|
return nil, fmt.Errorf("wedding.event_end is required in %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
return &cfg, nil
|
return &cfg, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export interface WeddingConfig {
|
export interface WeddingConfig {
|
||||||
siteUrl: string;
|
siteUrl: string;
|
||||||
weddingDate: string;
|
weddingDate: string;
|
||||||
|
eventEnd: string;
|
||||||
ceremonyTime: string;
|
ceremonyTime: string;
|
||||||
rsvpByDate: string;
|
rsvpByDate: string;
|
||||||
venueName: string;
|
venueName: string;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// fallback while /api/config loads (and when developing the UI without the Go server).
|
// fallback while /api/config loads (and when developing the UI without the Go server).
|
||||||
export const FALLBACK_SITE_URL = "https://zoeshaldon.warky.info";
|
export const FALLBACK_SITE_URL = "https://zoeshaldon.warky.info";
|
||||||
export const FALLBACK_WEDDING_DATE = "2026-11-07T16:00:00+02:00";
|
export const FALLBACK_WEDDING_DATE = "2026-11-07T16:00:00+02:00";
|
||||||
|
export const FALLBACK_EVENT_END = "2026-11-07T23:30:00+02:00";
|
||||||
export const FALLBACK_CEREMONY_TIME = "16:00 for 16:30";
|
export const FALLBACK_CEREMONY_TIME = "16:00 for 16:30";
|
||||||
export const FALLBACK_RSVP_BY_DATE = "2026-10-17T00:00:00+02:00";
|
export const FALLBACK_RSVP_BY_DATE = "2026-10-17T00:00:00+02:00";
|
||||||
export const FALLBACK_VENUE_NAME = "Rivier Plaas Wedding Venue";
|
export const FALLBACK_VENUE_NAME = "Rivier Plaas Wedding Venue";
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { fetchConfig } from './api';
|
|||||||
import {
|
import {
|
||||||
FALLBACK_SITE_URL,
|
FALLBACK_SITE_URL,
|
||||||
FALLBACK_WEDDING_DATE,
|
FALLBACK_WEDDING_DATE,
|
||||||
|
FALLBACK_EVENT_END,
|
||||||
FALLBACK_CEREMONY_TIME,
|
FALLBACK_CEREMONY_TIME,
|
||||||
FALLBACK_RSVP_BY_DATE,
|
FALLBACK_RSVP_BY_DATE,
|
||||||
FALLBACK_VENUE_NAME,
|
FALLBACK_VENUE_NAME,
|
||||||
@@ -13,6 +14,7 @@ import {
|
|||||||
export function createWeddingInfo() {
|
export function createWeddingInfo() {
|
||||||
let siteUrl = $state(FALLBACK_SITE_URL);
|
let siteUrl = $state(FALLBACK_SITE_URL);
|
||||||
let date = $state(new Date(FALLBACK_WEDDING_DATE));
|
let date = $state(new Date(FALLBACK_WEDDING_DATE));
|
||||||
|
let eventEnd = $state(new Date(FALLBACK_EVENT_END));
|
||||||
let ceremonyTime = $state(FALLBACK_CEREMONY_TIME);
|
let ceremonyTime = $state(FALLBACK_CEREMONY_TIME);
|
||||||
let rsvpByDate = $state(new Date(FALLBACK_RSVP_BY_DATE));
|
let rsvpByDate = $state(new Date(FALLBACK_RSVP_BY_DATE));
|
||||||
let venueName = $state(FALLBACK_VENUE_NAME);
|
let venueName = $state(FALLBACK_VENUE_NAME);
|
||||||
@@ -25,6 +27,7 @@ export function createWeddingInfo() {
|
|||||||
.then((config) => {
|
.then((config) => {
|
||||||
siteUrl = config.siteUrl;
|
siteUrl = config.siteUrl;
|
||||||
date = new Date(config.weddingDate);
|
date = new Date(config.weddingDate);
|
||||||
|
eventEnd = new Date(config.eventEnd);
|
||||||
ceremonyTime = config.ceremonyTime;
|
ceremonyTime = config.ceremonyTime;
|
||||||
rsvpByDate = new Date(config.rsvpByDate);
|
rsvpByDate = new Date(config.rsvpByDate);
|
||||||
venueName = config.venueName;
|
venueName = config.venueName;
|
||||||
@@ -46,6 +49,9 @@ export function createWeddingInfo() {
|
|||||||
get ceremonyTime() {
|
get ceremonyTime() {
|
||||||
return ceremonyTime;
|
return ceremonyTime;
|
||||||
},
|
},
|
||||||
|
get eventEnd() {
|
||||||
|
return eventEnd;
|
||||||
|
},
|
||||||
get rsvpByDate() {
|
get rsvpByDate() {
|
||||||
return rsvpByDate;
|
return rsvpByDate;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<main
|
<main
|
||||||
class="text-shadow-sm text-shadow-white/80 dark:text-shadow-black/70 flex flex-1 flex-col"
|
class="text-surface-900 dark:text-surface-100 text-shadow-sm text-shadow-white/80 dark:text-shadow-black/70 flex flex-1 flex-col"
|
||||||
>
|
>
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
const icsUrl = $derived(
|
const icsUrl = $derived(
|
||||||
buildIcsDataUrl({
|
buildIcsDataUrl({
|
||||||
start: wedding.date,
|
start: wedding.date,
|
||||||
end: new Date(wedding.date.getTime() + 7.5 * 60 * 60 * 1000),
|
end: wedding.eventEnd,
|
||||||
summary: "Zoé & Shaldon's Wedding",
|
summary: "Zoé & Shaldon's Wedding",
|
||||||
location: `${wedding.venueName}, ${wedding.venueAddress}`,
|
location: `${wedding.venueName}, ${wedding.venueAddress}`,
|
||||||
description: `Join us as we say I do! ${wedding.ceremonyTime}.`,
|
description: `Join us as we say I do! ${wedding.ceremonyTime}.`,
|
||||||
|
|||||||
Reference in New Issue
Block a user