feat: persist booked dates in JSON file and exclude booked dates from selection

This commit is contained in:
2026-08-12 18:10:53 +02:00
parent 8685b3c466
commit 23baff9ef0
6 changed files with 102 additions and 5 deletions
+1
View File
@@ -1,6 +1,7 @@
# Server Port & Configuration # Server Port & Configuration
PORT=8080 PORT=8080
WEB_DIR=public WEB_DIR=public
BOOKED_DATES_FILE=booked_dates.json
# SMTP Configuration # SMTP Configuration
SMTP_HOST=mail.example.com SMTP_HOST=mail.example.com
@@ -0,0 +1 @@
I now want the application to maintain a list of booked dates. That can be a simple text file or json file. every time a booking is submitted, the date should be added to that file. When offering available dates for selection, the application should read that file and exclude any dates that have already been booked.
+1
View File
@@ -1,2 +1,3 @@
.env .env
*.exe *.exe
booked_dates.json
+1
View File
@@ -41,6 +41,7 @@ Trage in der `.env` deine Server- und SMTP-Zugangsdaten ein:
| :--- | :--- | :--- | | :--- | :--- | :--- |
| `PORT` | HTTP-Port des Go-Servers | `8080` | | `PORT` | HTTP-Port des Go-Servers | `8080` |
| `WEB_DIR` | Verzeichnis der statischen Assets | `public` | | `WEB_DIR` | Verzeichnis der statischen Assets | `public` |
| `BOOKED_DATES_FILE` | Speicherpfad für bereits gebuchte Termine | `booked_dates.json` |
| `SMTP_HOST` | Postausgangsserver (SMTP) | `mail.example.com` | | `SMTP_HOST` | Postausgangsserver (SMTP) | `mail.example.com` |
| `SMTP_PORT` | SMTP-Port (Standard meist 587 oder 465) | `587` | | `SMTP_PORT` | SMTP-Port (Standard meist 587 oder 465) | `587` |
| `SMTP_USER` | Benutzername / E-Mail für SMTP-Auth | `absender@example.com` | | `SMTP_USER` | Benutzername / E-Mail für SMTP-Auth | `absender@example.com` |
+79
View File
@@ -46,6 +46,7 @@ func main() {
} }
http.HandleFunc("/api/submit", handleSubmit) http.HandleFunc("/api/submit", handleSubmit)
http.HandleFunc("/api/booked-dates", handleBookedDates)
// Serve static files from webDir directory // Serve static files from webDir directory
fs := http.FileServer(http.Dir(webDir)) fs := http.FileServer(http.Dir(webDir))
@@ -81,6 +82,10 @@ func handleSubmit(w http.ResponseWriter, r *http.Request) {
log.Printf("Received survey submission from %s for date %s (%s)", payload.Name, payload.Date, payload.Activity) log.Printf("Received survey submission from %s for date %s (%s)", payload.Name, payload.Date, payload.Activity)
if err := recordBookedDate(payload.Date); err != nil {
log.Printf("Error recording booked date: %v", err)
}
err = sendEmail(payload) err = sendEmail(payload)
if err != nil { if err != nil {
log.Printf("Error sending email: %v", err) log.Printf("Error sending email: %v", err)
@@ -101,6 +106,80 @@ func handleSubmit(w http.ResponseWriter, r *http.Request) {
}) })
} }
func handleBookedDates(w http.ResponseWriter, r *http.Request) {
if enableCORS(w, r) {
return
}
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
dates, err := getBookedDates()
if err != nil {
log.Printf("Error reading booked dates: %v", err)
dates = []string{}
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(dates)
}
func getBookedDatesFilePath() string {
path := os.Getenv("BOOKED_DATES_FILE")
if path == "" {
path = "booked_dates.json"
}
return path
}
func getBookedDates() ([]string, error) {
filePath := getBookedDatesFilePath()
file, err := os.Open(filePath)
if err != nil {
if os.IsNotExist(err) {
return []string{}, nil
}
return nil, err
}
defer file.Close()
var dates []string
if err := json.NewDecoder(file).Decode(&dates); err != nil {
return []string{}, nil
}
return dates, nil
}
func recordBookedDate(dateStr string) error {
if strings.TrimSpace(dateStr) == "" {
return nil
}
dates, err := getBookedDates()
if err != nil {
dates = []string{}
}
for _, d := range dates {
if d == dateStr {
return nil
}
}
dates = append(dates, dateStr)
filePath := getBookedDatesFilePath()
data, err := json.MarshalIndent(dates, "", " ")
if err != nil {
return err
}
return os.WriteFile(filePath, data, 0644)
}
func enableCORS(w http.ResponseWriter, r *http.Request) bool { func enableCORS(w http.ResponseWriter, r *http.Request) bool {
w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS") w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
+19 -5
View File
@@ -18,6 +18,7 @@
today.setHours(0, 0, 0, 0); today.setHours(0, 0, 0, 0);
let availableDatesMap = new Map(); let availableDatesMap = new Map();
let bookedDatesSet = new Set();
let viewYear = today.getFullYear(); let viewYear = today.getFullYear();
let viewMonth = today.getMonth(); let viewMonth = today.getMonth();
let selectedDate = null; let selectedDate = null;
@@ -36,15 +37,28 @@
return `${year}-${month}-${day}`; return `${year}-${month}-${day}`;
} }
fetch("appsettings.json") Promise.all([
.then((response) => response.json()) fetch("appsettings.json").then((res) => res.json()),
.then((settings) => { fetch("/api/booked-dates")
.then((res) => (res.ok ? res.json() : []))
.catch(() => []),
])
.then(([settings, bookedDates]) => {
bookedDatesSet = new Set(bookedDates || []);
const datesList = settings.availableDates || []; const datesList = settings.availableDates || [];
datesList.forEach((item) => { datesList.forEach((item) => {
let key = "";
let type = "long";
if (typeof item === "string") { if (typeof item === "string") {
availableDatesMap.set(item, { type: "long" }); key = item;
} else if (item && item.date) { } else if (item && item.date) {
availableDatesMap.set(item.date, { type: item.type || "long" }); key = item.date;
type = item.type || "long";
}
if (key && !bookedDatesSet.has(key)) {
availableDatesMap.set(key, { type });
} }
}); });