feat(ui): add wedding theme styles and layout components

* Introduced wedding-themed CSS variables for styling.
* Created layout component for the wedding site with navigation and countdown.
* Added RSVP and photo upload pages with form handling.
* Implemented QR code page for wedding site access.
* Configured TypeScript and Vite for the project.
* Added robots.txt for search engine crawling.
This commit is contained in:
2026-08-11 23:31:35 +02:00
commit 39f5b8d5cc
89 changed files with 6829 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
package config
import (
"fmt"
"os"
"time"
"gopkg.in/yaml.v3"
)
type Config struct {
Server ServerConfig `yaml:"server"`
Site SiteConfig `yaml:"site"`
Wedding WeddingConfig `yaml:"wedding"`
Storage StorageConfig `yaml:"storage"`
Email EmailConfig `yaml:"email"`
Upload UploadConfig `yaml:"upload"`
}
type ServerConfig struct {
Port int `yaml:"port"`
}
type SiteConfig struct {
URL string `yaml:"url"`
}
type WeddingConfig struct {
Date time.Time `yaml:"date"`
CeremonyTime string `yaml:"ceremony_time"`
RSVPBy time.Time `yaml:"rsvp_by"`
VenueName string `yaml:"venue_name"`
VenueAddress string `yaml:"venue_address"`
RSVPPhone string `yaml:"rsvp_phone"`
}
type StorageConfig struct {
DataDir string `yaml:"data_dir"`
}
type EmailConfig struct {
SMTPHost string `yaml:"smtp_host"`
SMTPPort int `yaml:"smtp_port"`
SMTPUser string `yaml:"smtp_user"`
SMTPPass string `yaml:"smtp_pass"`
From string `yaml:"from"`
To string `yaml:"to"`
}
type UploadConfig struct {
MaxSizeMB int `yaml:"max_size_mb"`
MaxFiles int `yaml:"max_files"`
}
// Path resolves the config file location: $CONFIG_PATH, or ./config.yaml.
func Path() string {
if p := os.Getenv("CONFIG_PATH"); p != "" {
return p
}
return "config.yaml"
}
func Load(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("reading config %s: %w", path, err)
}
var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parsing config %s: %w", path, err)
}
if cfg.Server.Port == 0 {
cfg.Server.Port = 8080
}
if cfg.Storage.DataDir == "" {
cfg.Storage.DataDir = "./data"
}
if cfg.Upload.MaxSizeMB == 0 {
cfg.Upload.MaxSizeMB = 15
}
if cfg.Upload.MaxFiles == 0 {
cfg.Upload.MaxFiles = 10
}
if cfg.Wedding.Date.IsZero() {
return nil, fmt.Errorf("wedding.date is required in %s", path)
}
if cfg.Wedding.RSVPBy.IsZero() {
return nil, fmt.Errorf("wedding.rsvp_by is required in %s", path)
}
return &cfg, nil
}