From a8adbdc116f83962f05957f41a38f0394432d49e Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Tue, 12 May 2026 20:16:46 -0400 Subject: [PATCH] Switch to orders.seahaven.com, add roster dropdown, fix deadline (#9) - Change custom domain from orders.seahavenind.com to orders.seahaven.com to match other subdomain conventions - Add GET /api/roster endpoint returning employee names/emails - Replace name/email text inputs with dropdown populated from roster API (falls back to embedded roster for local dev) - Fix order deadline text from Wednesday to Thursday 11:59 PM - Fix Slack API calls: use form-urlencoded for conversations and users methods that reject JSON body encoding --- README.md | 6 ++--- functions/submit_order/handler.py | 11 ++++++++- samconfig.toml.example | 2 +- src/server/generate_form.py | 40 +++++++++++++++++++++++++++---- template.yaml | 8 ++++++- 5 files changed, 57 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b6b8f68..7909b05 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ who haven't ordered | Monday 6:55am ET | Sync employee roster from Slack channel membership | EventBridge → Lambda → DynamoDB | | Monday 7am ET | Email previous week's payroll deductions to `payroll@` | EventBridge → Lambda → SES | | Monday 8am ET | Scrape menu, generate form, upload to S3, post link to Slack | GitHub Actions cron | -| Mon–Thu | Employees visit `orders.seahavenind.com` and submit orders | S3 static form → API Gateway → Lambda → DynamoDB | +| Mon–Thu | Employees visit `orders.seahaven.com` and submit orders | S3 static form → API Gateway → Lambda → DynamoDB | | Thursday 10am ET | DM employees who haven't ordered yet | EventBridge → Lambda → Slack DM | | Thursday 6pm ET | Close form, aggregate orders, post Redefine order summary to Slack | EventBridge → Lambda chain | @@ -41,7 +41,7 @@ who haven't ordered Stack name: `meal-order-manager` (us-east-1) - **S3** — `meal-order-manager-form-*` (static form hosting), `meal-order-manager-reports-*` (CSV reports) -- **CloudFront** — HTTPS distribution with custom domain `orders.seahavenind.com` +- **CloudFront** — HTTPS distribution with custom domain `orders.seahaven.com` - **DynamoDB** — `meal-order-manager-orders` (orders, menu, roster, config) - **API Gateway** — HttpApi for order submission - **Lambda** — 6 functions: submit-order, close-form, aggregate-orders, slack-notifier, sync-roster, email-report @@ -75,7 +75,7 @@ sam deploy 2. Create the form API key secret: `aws secretsmanager create-secret --name meal-order-manager/form-api-key --secret-string "$(openssl rand -hex 32)"` 3. Update the Slack channel SSM parameter: `aws ssm put-parameter --name /meal-order-manager/slack-channel-id --value "C0XXXXXXX" --overwrite` 4. Verify SES sender identity for `adam@seahavenind.com` -5. Set up DNS: CNAME `orders.seahavenind.com` → CloudFront distribution domain +5. Set up DNS: CNAME `orders.seahaven.com` → CloudFront distribution domain 6. Roster syncs automatically from Slack channel members (runs Monday 6:55am ET), or seed manually: `python3 scripts/seed_roster.py` ## Local Workflow (no AWS) diff --git a/functions/submit_order/handler.py b/functions/submit_order/handler.py index 9ff9939..2841c6b 100644 --- a/functions/submit_order/handler.py +++ b/functions/submit_order/handler.py @@ -3,7 +3,7 @@ import os from datetime import datetime from zoneinfo import ZoneInfo -from shared.db import current_week, get_form_status, put_order +from shared.db import current_week, get_form_status, get_roster, put_order from shared.secrets import get_secret EASTERN = ZoneInfo("America/New_York") @@ -24,6 +24,9 @@ def lambda_handler(event, context): if "/form-status/" in path: return handle_form_status(event) + if "/roster" in path: + return handle_roster() + if method == "POST": return handle_submit(event) @@ -36,6 +39,12 @@ def handle_form_status(event): return response(200, {"week": week, "status": status}) +def handle_roster(): + roster = get_roster() + employees = [{"name": e["name"], "email": e["email"]} for e in roster] + return response(200, {"employees": employees}) + + def handle_submit(event): api_key = event.get("headers", {}).get("x-api-key", "") if api_key != _get_api_key(): diff --git a/samconfig.toml.example b/samconfig.toml.example index 788af9b..328f48c 100644 --- a/samconfig.toml.example +++ b/samconfig.toml.example @@ -7,4 +7,4 @@ s3_prefix = "meal-order-manager" region = "us-east-1" confirm_changeset = true capabilities = "CAPABILITY_IAM" -parameter_overrides = "CustomDomain=orders.seahavenind.com CertificateArn=arn:aws:acm:us-east-1:328440206208:certificate/CHANGE-ME PayrollEmail=payroll@seahavenind.com SenderEmail=adam@seahavenind.com" +parameter_overrides = "CustomDomain=orders.seahaven.com CertificateArn=arn:aws:acm:us-east-1:328440206208:certificate/CHANGE-ME PayrollEmail=payroll@seahavenind.com SenderEmail=adam@seahavenind.com" diff --git a/src/server/generate_form.py b/src/server/generate_form.py index d67e642..e601e08 100644 --- a/src/server/generate_form.py +++ b/src/server/generate_form.py @@ -39,11 +39,12 @@ def generate_form( ) -> str: meals_json = json.dumps(menu["meals"]) roster_json = json.dumps(config.get("roster", [])) - deadline = config.get("order_deadline", "Thursday 6:00 PM") + deadline = config.get("order_deadline", "Thursday 11:59 PM") week = datetime.now().strftime("%Y-W%U") scraped_at = menu.get("scraped_at", "unknown") submit_url = f"{api_url}/api/submit-order" if api_url else "/api/submit-order" status_url = f"{api_url}/api/form-status/{week}" if api_url else "" + roster_url = f"{api_url}/api/roster" if api_url else "/api/roster" api_key_json = json.dumps(api_key) return f""" @@ -106,9 +107,8 @@ body {{ padding-bottom: 80px; }}