feat: persist booked dates in JSON file and exclude booked dates from selection
This commit is contained in:
@@ -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,2 +1,3 @@
|
|||||||
.env
|
.env
|
||||||
*.exe
|
*.exe
|
||||||
|
booked_dates.json
|
||||||
|
|||||||
@@ -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` |
|
||||||
|
|||||||
@@ -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
@@ -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 });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user