From a397a14cabbd2b89edc540292f038d75736cd6a3 Mon Sep 17 00:00:00 2001 From: popertots Date: Tue, 4 Nov 2025 00:23:51 +0000 Subject: [PATCH] Initial commit --- .gitignore | 82 + bm25_index.json | 5232 +++++++++++++++++++ compact_toon.py | 124 + enhanced_toon.py | 219 + include/site/python3.13/greenlet/greenlet.h | 164 + lib64 | 1 + mcp_codebase.py | 2333 +++++++++ package.json | 19 + quick_indexes.py | 86 + requirements.txt | 28 + serve_http.py | 255 + share/man/man1/isympy.1 | 188 + tools/parse_go_ast | Bin 0 -> 3406043 bytes tools/parse_go_ast.go | 194 + tools/parse_ts.js | 177 + 15 files changed, 9102 insertions(+) create mode 100644 .gitignore create mode 100644 bm25_index.json create mode 100644 compact_toon.py create mode 100644 enhanced_toon.py create mode 100644 include/site/python3.13/greenlet/greenlet.h create mode 120000 lib64 create mode 100644 mcp_codebase.py create mode 100644 package.json create mode 100644 quick_indexes.py create mode 100644 requirements.txt create mode 100644 serve_http.py create mode 100644 share/man/man1/isympy.1 create mode 100755 tools/parse_go_ast create mode 100644 tools/parse_go_ast.go create mode 100755 tools/parse_ts.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d55e5d --- /dev/null +++ b/.gitignore @@ -0,0 +1,82 @@ +# --- Python -------------------------------------------------------------- +venv/ +.env/ +.venv/ +env/ +__pycache__/ +*.py[cod] +*.pyo +*.pyd +*.so +*.dll +*.dylib + +# Build / distribution +build/ +dist/ +*.egg-info/ +.eggs/ +*.egg +*.whl +*.zip +*.tar.gz +*.tgz +*.rar +*.tar.bz2 +*.gz + +# Virtual environment activation scripts +activate* +activate.csh +activate.fish +activate.ps1 +activate +pyvenv.cfg + +# Coverage / testing +htmlcov/ +coverage.xml +.tox/ +.env +.env.* + +# --- Node / JavaScript ----------------------------------------------- +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json +yarn.lock + +# Transpiled output (if any) +dist/ +build/ +lib/ +src/ + +# --- OS / IDE --------------------------------------------------------- +.DS_Store +Thumbs.db +.idea/ +.vscode/ +*.swp +*.swo +*.bak +*.tmp +*.temp +*.orig +*.sublime-workspace +*.sublime-project + +# --- Binaries / compiled packages -------------------------------------- +lib/python3.13/site-packages/ +lib64/ +bin/ + +# --- Runtime data / large blobs --------------------------------------- +chroma_db/ +chroma.sqlite3 + +# --- Misc ---------------------------------------------------------------- +# (Leave this at the end to catch anything not matched above) +# (but **do NOT** put a blanket "*" here!) diff --git a/bm25_index.json b/bm25_index.json new file mode 100644 index 0000000..d335710 --- /dev/null +++ b/bm25_index.json @@ -0,0 +1,5232 @@ +{ + "corpus": [ + "File: /home/popertots/Crussell/local-dev.sh\nType: function\nName: create_booking\nComments:\nFunction to create a booking\n\nCode:\ncreate_booking() {\n local TOKEN=$1\n local START_TIME=$2\n local SERVICE_IDS_JSON=$3\n local NOTES=$4\n local BOOKING_NAME=$5\n \n local BOOKING_JSON=\"{\\\"start_time\\\":\\\"$START_TIME\\\",\\\"service_ids\\\":$SERVICE_IDS_JSON\"\n if [ -n \"$NOTES\" ]; then\n BOOKING_JSON=\"$BOOKING_JSON,\\\"notes\\\":\\\"$NOTES\\\"\"\n fi\n BOOKING_JSON=\"$BOOKING_JSON}\"\n \n echo \"\"\n echo \"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\"\n echo \"Creating: $BOOKING_NAME\"\n echo \"Time: $START_TIME\"\n echo \"Services: $SERVICE_IDS_JSON\"\n \n CREATE_RESPONSE=$(curl -s -w \"\\n%{http_code}\" -X POST \\\n -H 'Content-Type: application/json' \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -d \"$BOOKING_JSON\" \\\n \"$BASE_URL/bookings\")\n \n HTTP_CODE=$(echo \"$CREATE_RESPONSE\" | tail -n1)\n RESPONSE_BODY=$(echo \"$CREATE_RESPONSE\" | sed '$d')\n \n if [ \"$HTTP_CODE\" = \"201\" ]; then\n echo \"\u2705 Created: $BOOKING_NAME\"\n BOOKING_ID=$(echo \"$RESPONSE_BODY\" | grep -o '\"id\":\"[^\"]*' | cut -d'\"' -f4)\n echo \" Booking ID: $BOOKING_ID\"\n else\n echo \"\u274c Failed to create: $BOOKING_NAME (HTTP $HTTP_CODE)\"\n if [ -n \"$RESPONSE_BODY\" ]; then\n echo \"Response body: $RESPONSE_BODY\"\n fi\n fi\n \n sleep 0.2\n}\n", + "File: /home/popertots/Crussell/local-dev.sh\nType: function\nName: format_london_time\nComments:\nHelper function to format a London time as RFC3339 with 'T' (required by Go backend)\n\nCode:\nformat_london_time() {\n local DATE_PART=\"$1\"\n local TIME_PART=\"$2\"\n TZ=Europe/London date -d \"$DATE_PART $TIME_PART\" +\"%Y-%m-%dT%H:%M:%S%:z\"\n}\n", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- NAIL SALON DATABASE SCHEMA\n-- Complete init.sql with GDPR & Tax Compliance\n-- =======================================\n\n-- Enable pgcrypto for generating random IDs\nCREATE EXTENSION IF NOT EXISTS pgcrypto;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- ENUMS\n-- =======================================\n\nCREATE TYPE account_role AS ENUM ('unverified_email', 'verified_email', 'admin', 'guest', 'affiliate');", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: create type enum\nName: account_type\nSQL:\nCREATE TYPE account_type AS ENUM ('email', 'google', 'microsoft', 'facebook', 'guest');", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: create type enum\nName: payment_type\nSQL:\nCREATE TYPE payment_type AS ENUM ('deposit', 'full', 'tip', 'balance', 'partial');", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: create type enum\nName: payment_method\nSQL:\nCREATE TYPE payment_method AS ENUM ('online_square', 'in_person_card', 'cash', 'giftcard', 'discount');", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: create type enum\nName: payment_status\nSQL:\nCREATE TYPE payment_status AS ENUM ('pending', 'completed', 'failed', 'refunded');", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: create type enum\nName: booking_status\nSQL:\nCREATE TYPE booking_status AS ENUM ('pending', 'confirmed', 'in_progress', 'completed', 'client_cancelled', 'we_cancelled', 're-schedule', 'no_show');", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- SHORT ID GENERATION\n-- =======================================\n\nCREATE OR REPLACE FUNCTION generate_short_id(table_name TEXT)\nRETURNS CHAR(12) AS $$\nDECLARE\n new_id CHAR(12);\n collision_count INT := 0;\n max_attempts INT := 100;\nBEGIN\n LOOP\n new_id := substr(encode(gen_random_bytes(6), 'hex'), 1, 12);\n\n EXECUTE format('SELECT 1 FROM %I WHERE id = $1 LIMIT 1', table_name) USING new_id;\n\n IF NOT FOUND THEN\n RETURN new_id;\n END IF;\n\n collision_count := collision_count + 1;\n IF collision_count >= max_attempts THEN\n RAISE EXCEPTION 'Unable to generate unique ID after % attempts for table %', max_attempts, table_name;\n END IF;\n END LOOP;\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE OR REPLACE FUNCTION generate_user_id() RETURNS CHAR(12) AS $$ SELECT generate_short_id('users'); $$ LANGUAGE sql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE OR REPLACE FUNCTION generate_service_id() RETURNS CHAR(12) AS $$ SELECT generate_short_id('services'); $$ LANGUAGE sql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE OR REPLACE FUNCTION generate_booking_id() RETURNS CHAR(12) AS $$ SELECT generate_short_id('bookings'); $$ LANGUAGE sql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE OR REPLACE FUNCTION generate_payment_id() RETURNS CHAR(12) AS $$ SELECT generate_short_id('payments'); $$ LANGUAGE sql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE OR REPLACE FUNCTION generate_referral_code()\nRETURNS CHAR(12) AS $$\nDECLARE\n new_code CHAR(12);\n collision_count INT := 0;\n max_attempts INT := 100;\nBEGIN\n LOOP\n new_code := substr(encode(gen_random_bytes(6), 'hex'), 1, 12);\n\n -- Check against referral_code column in users table (not id column)\n PERFORM 1 FROM users WHERE referral_code = new_code LIMIT 1;\n\n IF NOT FOUND THEN\n RETURN new_code;\n END IF;\n\n collision_count := collision_count + 1;\n IF collision_count >= max_attempts THEN\n RAISE EXCEPTION 'Unable to generate unique referral code after % attempts', max_attempts;\n END IF;\n END LOOP;\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: create\nName: None\nSQL:\n-- =======================================\n-- USERS TABLE\n-- =======================================\n\nCREATE TABLE users (\n id CHAR(12) PRIMARY KEY DEFAULT generate_user_id(),\n -- Identity fields\n n_first_name VARCHAR(50) NOT NULL, -- vCard N.given\n n_last_name VARCHAR(50) NOT NULL, -- vCard N.family\n fn VARCHAR(120) GENERATED ALWAYS AS (n_first_name || ' ' || n_last_name) STORED, -- vCard FN\n email VARCHAR(255) UNIQUE, -- vCard EMAIL (nullable for social-only/guest)\n phone VARCHAR(20), -- vCard TEL\n date_of_birth DATE, -- vCard BDAY\n profile_pic_url TEXT, -- vCard PHOTO\n -- Account fields\n account_role account_role NOT NULL DEFAULT 'unverified_email', -- initial signup role\n account_type account_type NOT NULL DEFAULT 'email', -- initial signup type (info only)\n -- Security fields\n password_hash TEXT, -- NULL for pure social logins\n last_login_at TIMESTAMPTZ DEFAULT NOW(),\n -- Loyalty fields\n loyalty_stamps INT NOT NULL DEFAULT 0,\n referral_code CHAR(12) UNIQUE DEFAULT generate_referral_code(),\n -- GDPR fields\n privacy_policy_and_terms_consent BOOLEAN NOT NULL DEFAULT TRUE,\n policy_consent_updated_at TIMESTAMPTZ DEFAULT NOW(),\n data_retention_consent BOOLEAN NOT NULL DEFAULT TRUE,\n data_consent_updated_at TIMESTAMPTZ DEFAULT NOW(),\n -- Audit fields\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n -- staff fields\n notes TEXT\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE TABLE user_social_logins (\n id BIGSERIAL PRIMARY KEY,\n user_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,\n provider account_type NOT NULL CHECK (provider IN ('google', 'microsoft', 'facebook')),\n immutable_id TEXT NOT NULL, -- the stable provider user ID\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n\n UNIQUE (provider, immutable_id), -- prevents duplicate identities\n UNIQUE (user_id, provider) -- one account per provider per user\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_users_email_lower ON users (LOWER(email));", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_users_account_role ON users (account_role);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE TABLE user_referrals (\n referrer_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,\n referred_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,\n referred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n claimed_booking_id CHAR(12) REFERENCES bookings(id) ON DELETE SET NULL,\n PRIMARY KEY (referrer_id, referred_id)\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: generate_service_id\nSQL:\n-- =======================================\n-- SERVICES TABLE\n-- =======================================\n\nCREATE TABLE services (\n id CHAR(12) PRIMARY KEY DEFAULT generate_service_id(),\n name VARCHAR(100) NOT NULL,\n description TEXT,\n price NUMERIC(10,2) NOT NULL,\n duration_minutes INT NOT NULL,\n is_active BOOLEAN NOT NULL DEFAULT TRUE,\n patch_test_duration_hours INT NOT NULL DEFAULT 0,\n minimum_age_required INT NOT NULL DEFAULT 16,\n requires_manual_pricing BOOLEAN NOT NULL DEFAULT FALSE,\n requires_manual_duration BOOLEAN NOT NULL DEFAULT FALSE,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n created_by CHAR(12)\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_services_name ON services(name);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE TABLE user_service_patch_tests (\n id BIGSERIAL PRIMARY KEY,\n user_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,\n service_id CHAR(12) NOT NULL REFERENCES services(id) ON DELETE CASCADE,\n last_time TIMESTAMPTZ NOT NULL\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_service_patch_tests_userid ON user_service_patch_tests(user_id);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: generate_booking_id\nSQL:\n-- =======================================\n-- BOOKINGS TABLE\n-- =======================================\n\nCREATE TABLE bookings (\n id CHAR(12) PRIMARY KEY DEFAULT generate_booking_id(),\n user_id CHAR(12) REFERENCES users(id) ON DELETE SET NULL,\n start_time TIMESTAMPTZ NOT NULL,\n status booking_status NOT NULL DEFAULT 'pending',\n notes TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n created_by CHAR(12)\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_bookings_userid ON bookings(user_id);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_bookings_starttime ON bookings(start_time);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_bookings_status ON bookings(status);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_bookings_userid_starttime ON bookings(user_id, start_time);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- BOOKING SERVICES JUNCTION TABLE\n-- =======================================\n\nCREATE TABLE booking_services (\n booking_id CHAR(12) NOT NULL REFERENCES bookings(id) ON DELETE CASCADE,\n service_id CHAR(12) NOT NULL REFERENCES services(id) ON DELETE RESTRICT,\n override_price NUMERIC(10,2),\n override_duration_minutes INT,\n PRIMARY KEY (booking_id, service_id)\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- DEFAULT WORKING HOURS TABLE\n-- =======================================\nCREATE TABLE working_hours (\n weekday SMALLINT PRIMARY KEY, -- 0 = Monday, 6 = Sunday\n start_time TIME NOT NULL, -- e.g. 09:00\n end_time TIME NOT NULL, -- e.g. 17:00\n is_open BOOLEAN NOT NULL DEFAULT TRUE\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_working_hours_weekday ON working_hours(weekday);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nINSERT INTO working_hours VALUES (0, '00:00:00', '00:00:00', FALSE);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nINSERT INTO working_hours VALUES (1, '09:00:00', '17:00:00', TRUE);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nINSERT INTO working_hours VALUES (2, '09:00:00', '17:00:00', TRUE);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nINSERT INTO working_hours VALUES (3, '12:00:00', '20:00:00', TRUE);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nINSERT INTO working_hours VALUES (4, '09:00:00', '17:00:00', TRUE);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nINSERT INTO working_hours VALUES (5, '09:00:00', '17:00:00', TRUE);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nINSERT INTO working_hours VALUES (6, '00:00:00', '00:00:00', FALSE);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- EXCEPTIONAL WORKING HOURS\n-- =======================================\n\n-- Exceptional working hours groups (template)\nCREATE TABLE exceptional_working_hours_groups (\n id SERIAL PRIMARY KEY,\n name TEXT NOT NULL, -- e.g. \"Christmas Schedule\"\n description TEXT NOT NULL -- e.g. \"Extended hours for holiday period\"\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- Exceptional working hours (7 entries per group, one per weekday)\nCREATE TABLE exceptional_working_hours (\n id SERIAL PRIMARY KEY,\n group_id INT NOT NULL REFERENCES exceptional_working_hours_groups(id) ON DELETE CASCADE,\n weekday SMALLINT NOT NULL, -- 0 = Monday ... 6 = Sunday\n start_time TIME NOT NULL,\n end_time TIME NOT NULL,\n is_open BOOLEAN NOT NULL DEFAULT TRUE\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE UNIQUE INDEX idx_group_weekday ON exceptional_working_hours(group_id, weekday);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- Exceptional group applications (assign a group to a specific week)\nCREATE TABLE exceptional_group_applications (\n id SERIAL PRIMARY KEY,\n group_id INT NOT NULL REFERENCES exceptional_working_hours_groups(id) ON DELETE CASCADE,\n week_start DATE NOT NULL -- Monday of the week this group applies to\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE UNIQUE INDEX idx_group_application_week ON exceptional_group_applications(week_start);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- PAYMENTS TABLE\n-- =======================================\n\nCREATE SEQUENCE invoice_number_seq\n START WITH 1\n INCREMENT BY 1\n NO MINVALUE\n NO MAXVALUE\n CACHE 1;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: create\nName: None\nSQL:\nCREATE TABLE payments (\n id CHAR(12) PRIMARY KEY DEFAULT generate_payment_id(),\n booking_id CHAR(12) NOT NULL REFERENCES bookings(id) ON DELETE RESTRICT,\n payment_type payment_type NOT NULL,\n payment_method payment_method NOT NULL,\n vendor_code TEXT,\n invoice_number INT UNIQUE DEFAULT nextval('invoice_number_seq'),\n status payment_status NOT NULL DEFAULT 'pending',\n amount NUMERIC(10,2) NOT NULL,\n -- VAT fields (NULL until VAT registered - likely won't need but good to be ready for)\n is_vat_applicable BOOLEAN NOT NULL DEFAULT FALSE,\n vat_rate NUMERIC(5,2),\n vat_amount NUMERIC(10,2),\n net_amount NUMERIC(10,2),\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n created_by CHAR(12)\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_payments_bookingid ON payments(booking_id);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_payments_status ON payments(status);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_payments_createdat ON payments(created_at);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- BUSINESS SETTINGS TABLE (FOR COMPLIANCE)\n-- Stores legal business info for receipts, VAT status, currency, etc.\n-- =======================================\n\nCREATE TABLE business_settings (\n id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,\n business_name VARCHAR(255) NOT NULL,\n business_address TEXT NOT NULL,\n business_phone VARCHAR(20),\n business_email VARCHAR(255),\n vat_registration_number VARCHAR(20),\n is_vat_registered BOOLEAN NOT NULL DEFAULT FALSE,\n default_vat_rate NUMERIC(5,2) NOT NULL DEFAULT 20.00,\n currency_code CHAR(3) NOT NULL DEFAULT 'GBP',\n website_url TEXT,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- Add trigger to auto-update timestamp\nCREATE OR REPLACE FUNCTION update_business_settings_timestamp()\nRETURNS TRIGGER AS $$\nBEGIN\n NEW.updated_at = NOW();\n RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: create trigger\nName: trigger_update_business_settings_timestamp\nSQL:\nCREATE TRIGGER trigger_update_business_settings_timestamp\n BEFORE UPDATE ON business_settings\n FOR EACH ROW\n EXECUTE FUNCTION update_business_settings_timestamp();", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- Insert default row\nINSERT INTO business_settings (\n business_name,\n business_address,\n business_phone,\n business_email,\n vat_registration_number,\n is_vat_registered,\n default_vat_rate,\n currency_code,\n website_url\n) VALUES (\n 'Crussell Nail Art Studio',\n 'Address',\n '+44 131 123 4567',\n 'crussellnails@gmail.com',\n NULL, -- Set this when we register for VAT\n FALSE, -- Set to TRUE when we register for VAT\n 20.00,\n 'GBP',\n 'https://www.website.co.uk'\n);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_payments_booking_id_status ON payments(booking_id, status);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_bookings_start_time_status ON bookings(start_time, status);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_users_created_at ON users(created_at);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\nCREATE INDEX idx_payments_created_at_status ON payments(created_at, status);", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- GDPR COMPLIANCE FUNCTIONS\n-- =======================================\n\n/*\nGDPR COMPLIANCE NOTES:\n- Legal basis for booking data: Contract performance (Art 6(1)(b) GDPR)\n- Legal basis for optional data (marketing): Consent (Art 6(1)(a) GDPR)\n- Data retention: While consent remains and account is active\n- Right to be forgotten: anonymize_user() for registered users\n- Subject access rights: export_all_user_data() provides complete export\n*/\n\n-- Anonymize registered user (Right to be Forgotten)\n-- WHY: GDPR Article 17 - users can request data deletion\n-- WHEN: User requests account deletion\n-- OUTPUT: Converts personal data to anonymous placeholder\nCREATE OR REPLACE FUNCTION anonymize_user(target_id CHAR(12))\nRETURNS VOID AS $$\nBEGIN\n UPDATE users\n SET\n n_first_name = 'Deleted',\n n_last_name = 'User',\n email = CONCAT('deleted+', target_id, '@example.com'),\n phone = NULL,\n profile_pic_url = NULL,\n date_of_birth = NULL,\n account_role = 'guest',\n loyalty_stamps = 0,\n data_retention_consent = FALSE,\n data_consent_updated_at = NOW(),\n updated_at = NOW()\n WHERE id = target_id\n AND account_role != 'guest';\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- Fully delete guest user\n-- WHY: Guest accounts have no ongoing business relationship\n-- WHEN: Cleaning up temporary/incomplete accounts\n-- OUTPUT: Complete removal from database\nCREATE OR REPLACE FUNCTION delete_guest_user(target_id CHAR(12))\nRETURNS VOID AS $$\nBEGIN\n DELETE FROM users\n WHERE id = target_id\n AND account_role = 'guest';\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- Update consent\n-- WHY: GDPR requires tracking consent changes\n-- WHEN: User updates privacy preferences\n-- OUTPUT: Updates consent flags and timestamps\nCREATE OR REPLACE FUNCTION update_data_consent(target_id CHAR(12), consent BOOLEAN)\nRETURNS VOID AS $$\nBEGIN\n UPDATE users\n SET data_retention_consent = consent,\n data_consent_updated_at = NOW(),\n updated_at = NOW()\n WHERE id = target_id;\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- Complete Subject Access Request Export\n-- WHY: GDPR Article 15 - users have right to access their data\n-- WHEN: Customer requests \"what data do you have on me?\"\n-- OUTPUT: Complete JSON export of all user data\n-- USE: Call from Go API endpoint, return to user via email/download\nCREATE OR REPLACE FUNCTION export_all_user_data(target_user_id CHAR(12))\nRETURNS JSON AS $$\nDECLARE\n result JSON;\nBEGIN\n SELECT json_build_object(\n 'user_profile', (\n SELECT json_build_object(\n 'id', id,\n 'first_name', n_first_name,\n 'last_name', n_last_name,\n 'full_name', fn,\n 'email', email,\n 'phone', phone,\n 'date_of_birth', date_of_birth,\n 'profile_pic_url', profile_pic_url,\n 'account_role', account_role,\n 'loyalty_stamps', loyalty_stamps,\n 'data_retention_consent', data_retention_consent,\n 'data_consent_updated_at', data_consent_updated_at,\n 'created_at', created_at,\n 'updated_at', updated_at\n )\n FROM users WHERE id = target_user_id\n ),\n 'bookings', (\n SELECT COALESCE(json_agg(\n json_build_object(\n 'booking_id', b.id,\n 'start_time', b.start_time,\n 'status', b.status,\n 'created_at', b.created_at,\n 'updated_at', b.updated_at,\n 'created_by', b.created_by,\n 'updated_by', b.updated_by,\n 'services', (\n SELECT COALESCE(json_agg(\n json_build_object(\n 'service_id', s.id,\n 'name', s.name,\n 'description', s.description,\n 'price', s.price,\n 'duration_minutes', s.duration_minutes\n )\n ), '[]'::json)\n FROM booking_services bs\n JOIN services s ON bs.service_id = s.id\n WHERE bs.booking_id = b.id\n )\n )\n ), '[]'::json)\n FROM bookings b WHERE b.user_id = target_user_id\n ),\n 'payments', (\n SELECT COALESCE(json_agg(\n json_build_object(\n 'payment_id', p.id,\n 'booking_id', p.booking_id,\n 'payment_type', p.payment_type,\n 'payment_method', p.payment_method,\n 'vendor_code', p.vendor_code,\n 'status', p.status,\n 'amount', p.amount,\n 'created_at', p.created_at,\n 'updated_at', p.updated_at,\n 'created_by', p.created_by,\n 'updated_by', p.updated_by\n )\n ), '[]'::json)\n FROM payments p \n JOIN bookings b ON p.booking_id = b.id \n WHERE b.user_id = target_user_id\n ),\n 'export_metadata', json_build_object(\n 'exported_at', NOW(),\n 'exported_by', 'system',\n 'user_id', target_user_id,\n 'format_version', '1.0'\n )\n ) INTO result;\n \n RETURN result;\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- TAX COMPLIANCE FUNCTIONS\n-- =======================================\n\n/*\nTAX COMPLIANCE NOTES:\n- Below \u00a390k turnover: No VAT registration required\n- Above \u00a390k turnover: VAT registration mandatory, Making Tax Digital required\n- All businesses: Income tax records required (simplified from April 2026/2027)\n- VAT rate: 20% standard rate applies to nail services\n*/\n\n-- VAT Return Data (for Making Tax Digital submission)\n-- WHY: VAT registered businesses must submit quarterly VAT returns via MTD software\n-- WHEN: Every quarter if VAT registered (\u00a390k+ annual turnover)\n-- OUTPUT: Summary totals for VAT return - total sales, VAT charged, net sales\n-- USE: Export to QuickBooks/Xero for MTD submission to HMRC\nCREATE OR REPLACE FUNCTION get_vat_return_data(\n start_date DATE,\n end_date DATE\n)\nRETURNS TABLE (\n period_start DATE,\n period_end DATE,\n total_sales NUMERIC(12,2),\n total_vat_charged NUMERIC(12,2),\n net_sales NUMERIC(12,2),\n transaction_count BIGINT\n) AS $$\nBEGIN\n RETURN QUERY\n SELECT \n start_date as period_start,\n end_date as period_end,\n COALESCE(SUM(p.amount), 0) as total_sales,\n COALESCE(SUM(\n CASE\n -- If VAT is explicitly stored, use it\n WHEN p.vat_amount IS NOT NULL THEN p.vat_amount\n -- If business is VAT registered but no VAT breakdown, calculate it\n WHEN (SELECT is_vat_registered FROM business_settings WHERE id = 1) THEN \n ROUND(p.amount - (p.amount / (1 + COALESCE(p.vat_rate, \n (SELECT default_vat_rate FROM business_settings WHERE id = 1))/100)), 2)\n -- Business not VAT registered = no VAT charged\n ELSE 0\n END\n ), 0) as total_vat_charged,\n COALESCE(SUM(\n CASE \n -- If net amount is explicitly stored, use it\n WHEN p.net_amount IS NOT NULL THEN p.net_amount\n -- If business is VAT registered but no net amount, calculate it\n WHEN (SELECT is_vat_registered FROM business_settings WHERE id = 1) THEN\n ROUND(p.amount / (1 + COALESCE(p.vat_rate, \n (SELECT default_vat_rate FROM business_settings WHERE id = 1))/100), 2)\n -- Business not VAT registered = gross amount is net amount\n ELSE p.amount\n END\n ), 0) as net_sales,\n COUNT(*) as transaction_count\n FROM payments p\n JOIN bookings b ON p.booking_id = b.id\n WHERE p.status = 'completed'\n AND p.created_at::date BETWEEN start_date AND end_date\n AND p.payment_type IN ('full', 'partial', 'balance');\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- Detailed Transaction Export (Primary tax function)\n-- WHY: All businesses need transaction records for tax returns and accounting\n-- WHEN: Monthly/quarterly for accounting software import\n-- OUTPUT: CSV-compatible transaction list with customer, service, payment details\n-- USE: Import directly into QuickBooks, Xero, or other accounting software\n-- PARAMETERS: include_vat=false (default for micro businesses), include_vat=true only when VAT registered\nCREATE OR REPLACE FUNCTION export_sales_transactions(\n start_date DATE,\n end_date DATE,\n include_vat BOOLEAN DEFAULT FALSE\n)\nRETURNS TABLE (\n transaction_date DATE,\n invoice_number TEXT,\n customer_name TEXT,\n customer_email TEXT,\n service_description TEXT,\n payment_method TEXT,\n gross_amount NUMERIC(10,2),\n net_amount NUMERIC(10,2),\n vat_amount NUMERIC(10,2),\n vat_rate NUMERIC(5,2),\n booking_id CHAR(12),\n payment_id CHAR(12)\n) AS $$\nBEGIN\n RETURN QUERY\n SELECT \n p.created_at::date as transaction_date,\n p.invoice_number::text AS invoice_number,\n COALESCE(u.fn, 'Walk-in Customer') as customer_name,\n u.email as customer_email,\n string_agg(s.name, ', ') as service_description,\n p.payment_method::text as payment_method,\n p.amount as gross_amount,\n CASE \n WHEN include_vat AND p.net_amount IS NOT NULL THEN p.net_amount\n WHEN include_vat THEN ROUND(p.amount / 1.20, 2)\n ELSE p.amount\n END as net_amount,\n CASE \n WHEN include_vat AND p.vat_amount IS NOT NULL THEN p.vat_amount\n WHEN include_vat THEN ROUND(p.amount - (p.amount / 1.20), 2)\n ELSE 0\n END as vat_amount,\n CASE \n WHEN include_vat THEN COALESCE(p.vat_rate, 20.00)\n ELSE NULL\n END as vat_rate,\n b.id as booking_id,\n p.id as payment_id\n FROM payments p\n JOIN bookings b ON p.booking_id = b.id\n LEFT JOIN users u ON b.user_id = u.id\n LEFT JOIN booking_services bs ON b.id = bs.booking_id\n LEFT JOIN services s ON bs.service_id = s.id\n WHERE p.status = 'completed'\n AND p.created_at::date BETWEEN start_date AND end_date\n AND p.payment_type IN ('full', 'partial', 'balance')\n GROUP BY p.id, b.id, u.fn, u.email, p.created_at, p.amount, p.net_amount, p.vat_amount, p.vat_rate, p.payment_method, p.invoice_number\n ORDER BY p.created_at;\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- Monthly Business Summary\n-- WHY: Track business performance trends and payment method preferences\n-- WHEN: Monthly review of business performance\n-- OUTPUT: Month-by-month breakdown of revenue, bookings, payment methods\n-- USE: Personal business insights - \"How am I doing vs last month?\"\nCREATE OR REPLACE FUNCTION get_monthly_business_summary(\n start_date DATE,\n end_date DATE\n)\nRETURNS TABLE (\n month_year TEXT,\n total_bookings BIGINT,\n total_revenue NUMERIC(12,2),\n cash_payments NUMERIC(12,2),\n card_payments NUMERIC(12,2),\n online_payments NUMERIC(12,2),\n avg_transaction NUMERIC(10,2)\n) AS $$\nBEGIN\n RETURN QUERY\n SELECT \n TO_CHAR(p.created_at, 'YYYY-MM') as month_year,\n COUNT(DISTINCT b.id) as total_bookings,\n SUM(p.amount) as total_revenue,\n SUM(CASE WHEN p.payment_method = 'cash' THEN p.amount ELSE 0 END) as cash_payments,\n SUM(CASE WHEN p.payment_method = 'in_person_card' THEN p.amount ELSE 0 END) as card_payments,\n SUM(CASE WHEN p.payment_method = 'online_square' THEN p.amount ELSE 0 END) as online_payments,\n ROUND(AVG(p.amount), 2) as avg_transaction\n FROM payments p\n JOIN bookings b ON p.booking_id = b.id\n WHERE p.status = 'completed'\n AND p.created_at::date BETWEEN start_date AND end_date\n AND p.payment_type IN ('full', 'partial', 'balance')\n GROUP BY TO_CHAR(p.created_at, 'YYYY-MM')\n ORDER BY month_year;\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- Simple Sales Totals (Quick business snapshot)\n-- WHY: Quick overview of current performance for dashboard/API\n-- WHEN: Dashboard loading, daily/weekly check-ins\n-- OUTPUT: Total sales, transaction count, payment method breakdown\n-- USE: Homepage dashboard, \"How much did I make this week?\" queries\nCREATE OR REPLACE FUNCTION get_sales_totals(\n start_date DATE,\n end_date DATE\n)\nRETURNS TABLE (\n total_sales NUMERIC(12,2),\n total_transactions BIGINT,\n cash_total NUMERIC(12,2),\n card_total NUMERIC(12,2),\n online_total NUMERIC(12,2)\n) AS $$\nBEGIN\n RETURN QUERY\n SELECT \n COALESCE(SUM(p.amount), 0) as total_sales,\n COUNT(*) as total_transactions,\n COALESCE(SUM(CASE WHEN p.payment_method = 'cash' THEN p.amount ELSE 0 END), 0) as cash_total,\n COALESCE(SUM(CASE WHEN p.payment_method = 'in_person_card' THEN p.amount ELSE 0 END), 0) as card_total,\n COALESCE(SUM(CASE WHEN p.payment_method = 'online_square' THEN p.amount ELSE 0 END), 0) as online_total\n FROM payments p\n JOIN bookings b ON p.booking_id = b.id\n WHERE p.status = 'completed'\n AND p.created_at::date BETWEEN start_date AND end_date\n AND p.payment_type IN ('full', 'partial', 'balance');\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- UTILITY FUNCTIONS\n-- =======================================\n\n-- VAT Registration Transition Function\n-- WHY: When crossing \u00a390k threshold, must register for VAT and backfill existing data\n-- WHEN: One-time use when VAT registration becomes mandatory\n-- OUTPUT: Updates all historical payments with VAT breakdown + sets new defaults\n-- USE: Call once when we register for VAT - transforms business from non-VAT to VAT\n-- =======================================\n-- UTILITY FUNCTIONS\n-- =======================================\n\n-- VAT Registration Transition Function\n-- WHY: When crossing \u00a390k threshold, must register for VAT and backfill existing data\n-- WHEN: One-time use when VAT registration becomes mandatory\n-- OUTPUT: Updates all historical payments with VAT breakdown + sets new defaults\n-- USE: Call once when we register for VAT - transforms business from non-VAT to VAT\nCREATE OR REPLACE FUNCTION enable_vat_registration(\n registration_date DATE DEFAULT CURRENT_DATE,\n vat_rate NUMERIC(5,2) DEFAULT 20.00,\n vat_reg_number TEXT DEFAULT NULL\n)\nRETURNS TABLE (\n payments_updated INT,\n total_vat_calculated NUMERIC(12,2),\n total_net_calculated NUMERIC(12,2),\n registration_effective_date DATE,\n settings_updated BOOLEAN\n) AS $$\nDECLARE\n updated_count INT;\n total_vat NUMERIC(12,2);\n total_net NUMERIC(12,2);\n settings_ok BOOLEAN := FALSE;\nBEGIN\n -- Update all existing completed payments with VAT breakdown\n UPDATE payments \n SET \n vat_rate = enable_vat_registration.vat_rate,\n vat_amount = ROUND(amount - (amount / (1 + enable_vat_registration.vat_rate/100)), 2),\n net_amount = ROUND(amount / (1 + enable_vat_registration.vat_rate/100), 2),\n is_vat_applicable = TRUE,\n updated_at = NOW()\n WHERE status = 'completed'\n AND created_at::date >= registration_date\n AND vat_amount IS NULL;\n \n GET DIAGNOSTICS updated_count = ROW_COUNT;\n \n -- Calculate totals\n SELECT \n COALESCE(SUM(vat_amount), 0),\n COALESCE(SUM(net_amount), 0)\n INTO total_vat, total_net\n FROM payments \n WHERE status = 'completed' \n AND created_at::date >= registration_date\n AND vat_amount IS NOT NULL;\n \n -- Update business_settings to reflect VAT registration\n UPDATE business_settings\n SET \n is_vat_registered = TRUE,\n vat_registration_number = COALESCE(vat_reg_number, vat_registration_number),\n default_vat_rate = enable_vat_registration.vat_rate,\n updated_at = NOW()\n WHERE id = 1;\n \n IF FOUND THEN\n settings_ok := TRUE;\n END IF;\n\n RETURN QUERY SELECT \n updated_count,\n total_vat,\n total_net,\n registration_date,\n settings_ok;\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- Update payment with VAT (for new payments after VAT registration)\n-- WHY: After VAT registration, all new payments need VAT calculation\n-- WHEN: Called automatically when processing new payments (if VAT registered)\n-- OUTPUT: Updates individual payment with VAT breakdown\n-- USE: Call from Go when processing new payments after VAT registration\n-- =======================================\n-- UPDATE PAYMENT WITH VAT\n-- =======================================\n-- WHY: After VAT registration, all new payments need VAT calculation\n-- WHEN: Called automatically when processing new payments (if VAT registered)\n-- OUTPUT: Updates individual payment with VAT breakdown\n-- USE: Call from Go or backend when processing new payments\nCREATE OR REPLACE FUNCTION apply_vat_to_payment(\n payment_id CHAR(12),\n vat_rate NUMERIC(5,2) DEFAULT NULL -- NULL = use default from business_settings\n)\nRETURNS VOID AS $$\nDECLARE\n effective_rate NUMERIC(5,2);\nBEGIN\n -- Determine effective VAT rate\n IF vat_rate IS NULL THEN\n SELECT default_vat_rate INTO effective_rate\n FROM business_settings\n WHERE id = 1 AND is_vat_registered = TRUE;\n\n IF NOT FOUND OR effective_rate IS NULL THEN\n RAISE EXCEPTION 'no vat rate provided and business is not registered for vat';\n END IF;\n ELSE\n effective_rate := vat_rate;\n END IF;\n\n -- Update payment with VAT amounts\n UPDATE payments\n SET \n vat_rate = effective_rate,\n vat_amount = ROUND(amount - (amount / (1 + effective_rate/100)), 2),\n net_amount = ROUND(amount / (1 + effective_rate/100), 2),\n is_vat_applicable = TRUE,\n updated_at = NOW()\n WHERE id = payment_id\n AND vat_amount IS NULL;\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- SIMPLE VAT CALCULATION HELPER\n-- =======================================\n-- WHY: Manual VAT calculations or validation\n-- WHEN: Checking VAT calculations or manual entry corrections\n-- OUTPUT: net amount and vat amount from gross amount\n-- USE: Manual calculations, validation, or corrections\nCREATE OR REPLACE FUNCTION calculate_vat(\n gross_amount NUMERIC(10,2),\n vat_rate NUMERIC(5,2) DEFAULT NULL -- NULL = use default from business_settings\n)\nRETURNS TABLE(\n net NUMERIC(10,2),\n vat NUMERIC(10,2)\n) AS $$\nDECLARE\n effective_rate NUMERIC(5,2);\nBEGIN\n -- Determine effective VAT rate\n IF vat_rate IS NULL THEN\n SELECT default_vat_rate INTO effective_rate\n FROM business_settings\n WHERE id = 1 AND is_vat_registered = TRUE;\n\n IF NOT FOUND OR effective_rate IS NULL THEN\n RAISE EXCEPTION 'no vat rate provided and business is not registered for vat';\n END IF;\n ELSE\n effective_rate := vat_rate;\n END IF;\n\n -- Return net and VAT\n RETURN QUERY\n SELECT \n ROUND(gross_amount / (1 + effective_rate/100), 2) AS net,\n gross_amount - ROUND(gross_amount / (1 + effective_rate/100), 2) AS vat;\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: statement\nName: None\nSQL:\n-- =======================================\n-- UK RECEIPT COMPLIANCE FUNCTION\n-- =======================================\n\n/*\nUK RECEIPT REQUIREMENTS:\n- Business name and address (static - provided by application)\n- Date and time of transaction\n- Description of goods/services\n- Amount charged (including VAT if registered)\n- VAT breakdown (if VAT registered)\n- Receipt/invoice number\n- Payment method\n- Customer details (if requested)\n\nVAT RECEIPT REQUIREMENTS (if VAT registered):\n- VAT registration number (static - provided by application)\n- VAT rate applied\n- VAT amount\n- Net amount (excluding VAT)\n- Total amount (including VAT)\n*/\n\n-- Complete Receipt Data for UK Compliance\n-- WHY: Generate legally compliant receipts for customers\n-- WHEN: After each completed payment/booking\n-- OUTPUT: All data needed for receipt printing/email\n-- USE: Call from Go API to generate customer receipts\nCREATE OR REPLACE FUNCTION get_receipt_data(payment_id CHAR(12))\nRETURNS TABLE (\n -- Business Information\n business_name TEXT,\n business_address TEXT,\n business_phone TEXT,\n business_email TEXT,\n vat_registration_number TEXT,\n is_vat_registered BOOLEAN,\n \n -- Payment Information\n payment_id CHAR(12),\n invoice_number TEXT,\n transaction_date TIMESTAMPTZ,\n payment_method TEXT,\n payment_status TEXT,\n \n -- Customer Information\n customer_id CHAR(12),\n customer_name TEXT,\n customer_email TEXT,\n customer_phone TEXT,\n \n -- Booking Information\n booking_id CHAR(12),\n appointment_date TIMESTAMPTZ,\n booking_status TEXT,\n \n -- Service Details\n services JSON,\n total_duration_minutes INT,\n \n -- Financial Information\n gross_amount NUMERIC(10,2),\n net_amount NUMERIC(10,2),\n vat_amount NUMERIC(10,2),\n vat_rate NUMERIC(5,2),\n is_vat_applicable BOOLEAN,\n \n -- Receipt Metadata\n receipt_generated_at TIMESTAMPTZ,\n currency_code TEXT\n) AS $$\nDECLARE\n bs RECORD; -- To hold business settings\nBEGIN\n -- Fetch current business settings (assume single row, id=1)\n SELECT INTO bs\n business_name,\n business_address,\n business_phone,\n business_email,\n vat_registration_number,\n is_vat_registered,\n currency_code\n FROM business_settings\n WHERE id = 1;\n\n RETURN QUERY\n SELECT \n -- Business Info\n bs.business_name,\n bs.business_address,\n bs.business_phone,\n bs.business_email,\n bs.vat_registration_number,\n bs.is_vat_registered,\n \n -- Payment Info\n p.id as payment_id,\n COALESCE('INV-' || p.invoice_number::text, 'INV-' || p.id) as invoice_number,\n p.created_at as transaction_date,\n p.payment_method::text as payment_method,\n p.status::text as payment_status,\n \n -- Customer Info\n u.id as customer_id,\n CASE \n WHEN u.account_role = 'guest' OR u.fn = 'Deleted User' THEN 'Walk-in Customer'\n ELSE u.fn\n END as customer_name,\n CASE \n WHEN u.account_role = 'guest' OR u.fn = 'Deleted User' THEN NULL\n ELSE u.email\n END as customer_email,\n CASE \n WHEN u.account_role = 'guest' OR u.fn = 'Deleted User' THEN NULL\n ELSE u.phone\n END as customer_phone,\n \n -- Booking Info\n b.id as booking_id,\n b.start_time as appointment_date,\n b.status::text as booking_status,\n \n -- Service Details\n COALESCE((\n SELECT json_agg(\n json_build_object(\n 'service_id', s.id,\n 'name', s.name,\n 'description', s.description,\n 'price', s.price,\n 'duration_minutes', s.duration_minutes\n )\n ORDER BY s.name\n )\n FROM booking_services bs_inner\n JOIN services s ON bs_inner.service_id = s.id\n WHERE bs_inner.booking_id = b.id\n ), '[]'::json) as services,\n \n -- Total service duration\n COALESCE((\n SELECT SUM(s.duration_minutes)\n FROM booking_services bs_inner\n JOIN services s ON bs_inner.service_id = s.id\n WHERE bs_inner.booking_id = b.id\n ), 0) as total_duration_minutes,\n \n -- Financial Info\n p.amount as gross_amount,\n COALESCE(p.net_amount, p.amount) as net_amount,\n COALESCE(p.vat_amount, 0.00) as vat_amount,\n p.vat_rate as vat_rate,\n p.is_vat_applicable as is_vat_applicable,\n \n -- Receipt Metadata\n NOW() as receipt_generated_at,\n bs.currency_code as currency_code\n \n FROM payments p\n JOIN bookings b ON p.booking_id = b.id\n LEFT JOIN users u ON b.user_id = u.id\n WHERE p.id = payment_id;\nEND;\n$$ LANGUAGE plpgsql;", + "File: /home/popertots/Crussell/init-scripts/init-script.sql\nType: unknown\nName: None\nSQL:\n-- =======================================\n-- FUNCTION USAGE SUMMARY\n-- =======================================\n/*\nThis summary organizes all database functions by their primary purpose, legal basis, and expected usage frequency\u2014\nproviding a clear guide for integration into our application, compliance workflows, and business operations.\n\n--------------------------------------------------------------------------------\n1. FINANCIAL & TAX COMPLIANCE\n--------------------------------------------------------------------------------\n\nREGULAR USE (called from API endpoints or UI):\n- get_sales_totals(start_date, end_date)\n \u2192 Quick dashboard metrics: total revenue, transaction count, and payment method breakdown.\n- get_receipt_data(payment_id)\n \u2192 Generate a UK-compliant receipt after every completed payment.\n- calculate_vat(gross_amount, vat_rate)\n \u2192 Utility for validating or manually computing VAT splits.\n\nPERIODIC USE (monthly/quarterly for accounting or tax filing):\n- export_sales_transactions(start_date, end_date, include_vat)\n \u2192 Primary export for accounting software (QuickBooks, Xero). Set include_vat = true only if VAT-registered.\n- get_monthly_business_summary(start_date, end_date)\n \u2192 Analyze trends: bookings, revenue, avg. transaction size, and payment method adoption.\n- get_vat_return_data(start_date, end_date)\n \u2192 MTD-ready VAT return summary for HMRC (only if VAT-registered).\n\nONE-TIME OR TRANSITIONAL USE:\n- enable_vat_registration(registration_date, vat_rate, vat_reg_number)\n \u2192 Run once when crossing the \u00a390k VAT threshold to backfill historical payments.\n- apply_vat_to_payment(payment_id, vat_rate)\n \u2192 Called automatically for every new payment after VAT registration.\n\n--------------------------------------------------------------------------------\n2. GDPR & DATA PRIVACY\n--------------------------------------------------------------------------------\n\nON-DEMAND USE (triggered by user request or admin action):\n- export_all_user_data(user_id)\n \u2192 Full Subject Access Request (SAR) export in JSON (GDPR Article 15).\n- anonymize_user(user_id)\n \u2192 Right to Erasure for registered users (GDPR Article 17); preserves audit trail.\n- delete_guest_user(user_id)\n \u2192 Complete deletion of guest accounts (no contractual basis).\n- update_data_consent(user_id, consent)\n \u2192 Records updated consent preference with timestamp for auditability.\n\nNOTE: Booking data is retained under contractual necessity (GDPR Art. 6(1)(b)) even if consent is withdrawn.\nOnly optional data (e.g., marketing) is governed by consent.\n\n--------------------------------------------------------------------------------\n3. BUSINESS OPERATIONS & LOYALTY\n--------------------------------------------------------------------------------\n\n- Referral Program: Use user_referrals table + referral_code in users (handled in app logic).\n- Patch Test Tracking: user_service_patch_tests enforces allergen safety (enforced in app based on services.patch_test_duration_hours).\n\n--------------------------------------------------------------------------------\n4. UTILITY & MAINTENANCE\n--------------------------------------------------------------------------------\n\n- generate_*_id() functions: Internal use only (DEFAULT in table definitions).\n- Timestamp triggers (e.g., on business_settings): Automatic\u2014no manual call needed.\n\n--------------------------------------------------------------------------------\nINTEGRATION QUICK REFERENCE\n--------------------------------------------------------------------------------\n\u2022 User deletes account \u2192 anonymize_user() or delete_guest_user()\n\u2022 \"What data do you have?\" \u2192 export_all_user_data()\n\u2022 New payment \u2192 apply_vat_to_payment() (if VAT reg) + get_receipt_data()\n\u2022 Monthly close \u2192 export_sales_transactions(..., include_vat := [true/false])\n\u2022 VAT registration day \u2192 enable_vat_registration() once, then apply_vat_to_payment()\n\u2022 Dashboard load \u2192 get_sales_totals() + get_monthly_business_summary()\n*/", + "File: /home/popertots/Crussell/backend/main.go\nType: func\nName: init\nCode:\nfunc init() {\n\tjwtSecret := os.Getenv(\"JWT_SECRET_KEY\")\n\tif jwtSecret == \"\" {\n\t\tlog.Fatal(\"FATAL: JWT_SECRET_KEY environment variable not set. Application cannot start.\")\n\t}\n\tauth.InitJWT(jwtSecret)\n}\n", + "File: /home/popertots/Crussell/backend/main.go\nType: func\nName: initDB\nCode:\nfunc initDB() {\n\tif err := db.Connect(); err != nil {\n\t\tlog.Fatal(\"Failed to connect to DB:\", err)\n\t}\n\tfmt.Println(\"Connected to DB successfully\")\n}\n", + "File: /home/popertots/Crussell/backend/main.go\nType: func\nName: initDav\nCode:\nfunc initDav() {\n\tif dav.Service == nil {\n\t\tlog.Fatal(\"Failed to initialize DAV service\")\n\t}\n\tfmt.Println(\"DAV Service connected successfully\")\n}\n", + "File: /home/popertots/Crussell/backend/main.go\nType: func\nName: main\nCode:\nfunc main() {\n\tinitDB()\n\tinitDav()\n\n\tr := chi.NewRouter()\n\n\t// --- Global Middleware ---\n\tr.Use(middleware.RequestID)\n\tr.Use(middleware.RealIP)\n\tr.Use(middleware.Logger)\n\tr.Use(middleware.Recoverer)\n\tr.Use(middleware.Timeout(15 * time.Second))\n\tr.Use(func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tw.Header().Set(\"X-Content-Type-Options\", \"nosniff\")\n\t\t\tw.Header().Set(\"X-Frame-Options\", \"DENY\")\n\t\t\tw.Header().Set(\"X-XSS-Protection\", \"1; mode=block\")\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t})\n\n\t// All API routes grouped under /api for clarity\n\tr.Route(\"/api\", func(r chi.Router) {\n\n\t\t// --- Public Routes ---\n\t\tr.Get(\"/services\", services.ServicesHandler)\n\t\tr.Post(\"/register\", authHandlers.RegisterHandler)\n\t\tr.Post(\"/login\", authHandlers.LoginHandler)\n\n\t\t// --- Scheduling Routes ---\n\t\tr.Route(\"/scheduling\", func(r chi.Router) {\n\t\t\t// Public GET routes\n\t\t\tr.Get(\"/default-hours\", scheduling.GetDefaultHours)\n\t\t\tr.Get(\"/exceptional-groups\", scheduling.ListExceptionalGroups)\n\t\t\tr.Get(\"/working-hours\", scheduling.GetWorkingHours)\n\t\t\tr.Get(\"/available-hours\", scheduling.GetAvailableHours)\n\n\t\t\t// Admin-only scheduling modifications\n\t\t\tr.Group(func(r chi.Router) {\n\t\t\t\tr.Use(mw.RequireAuth)\n\t\t\t\tr.Use(mw.RequireAdmin)\n\n\t\t\t\tr.Put(\"/default-hours\", scheduling.UpdateDefaultHours)\n\t\t\t\tr.Post(\"/exceptional-groups\", scheduling.CreateExceptionalGroup)\n\t\t\t\tr.Delete(\"/exceptional-groups\", scheduling.DeleteExceptionalGroup)\n\t\t\t\tr.Put(\"/exceptional-applications\", scheduling.UpdateExceptionalApplications)\n\t\t\t})\n\t\t})\n\n\t\t// --- Protected routes (any authenticated user) ---\n\t\tr.Group(func(r chi.Router) {\n\t\t\tr.Use(mw.RequireAuth)\n\n\t\t\tr.Get(\"/user/profile\", user.GetProfileHandler)\n\t\t\tr.Put(\"/user/profile\", user.UpdateProfileHandler)\n\t\t\tr.Delete(\"/user/account\", user.DeleteAccountHandler)\n\t\t\tr.Get(\"/user/loyalty\", user.GetLoyaltyHandler)\n\n\t\t\t// Booking routes for authenticated users\n\t\t\tr.Route(\"/bookings\", func(r chi.Router) {\n\t\t\t\tr.Get(\"/\", bookings.GetAllUserBookingsHandler)\n\t\t\t\tr.Post(\"/\", bookings.CreateBookingHandler)\n\t\t\t\tr.Get(\"/{id}\", bookings.GetBookingHandler)\n\t\t\t\tr.Put(\"/{id}\", bookings.EditBookingHandler)\n\t\t\t\tr.Delete(\"/{id}\", bookings.DeleteBookingHandler)\n\t\t\t})\n\t\t})\n\n\t\t// --- Admin-only routes ---\n\t\tr.Group(func(r chi.Router) {\n\t\t\tr.Use(mw.RequireAuth)\n\t\t\tr.Use(mw.RequireAdmin)\n\n\t\t\tr.Route(\"/admin/services\", func(r chi.Router) {\n\t\t\t\tr.Post(\"/\", services.CreateServiceHandler)\n\t\t\t\tr.Delete(\"/{id}\", services.DeleteServiceHandler)\n\t\t\t\tr.Get(\"/\", services.AllServicesHandler)\n\t\t\t\tr.Put(\"/{id}/toggle\", services.ToggleService)\n\t\t\t})\n\n\t\t\tr.Route(\"/admin/bookings\", func(r chi.Router) {\n\t\t\t\tr.Get(\"/\", bookings.GetAllAdminBookingsHandler)\n\t\t\t\tr.Get(\"/search\", bookings.SearchAdminBookingsHandler)\n\t\t\t\tr.Get(\"/user/{user_id}\", bookings.GetAllBookingsByUserHandler)\n\t\t\t\tr.Get(\"/{id}\", bookings.GetAdminBookingHandler)\n\t\t\t\tr.Put(\"/{id}/progress\", bookings.ProgressBookingHandler)\n\t\t\t\tr.Post(\"/{id}/confirm\", bookings.ConfirmBookingHandler)\n\t\t\t})\n\t\t})\n\t})\n\n\tfmt.Println(\"Server is listening on :8080\")\n\thttp.ListenAndServe(\":8080\", r)\n}\n", + "File: /home/popertots/Crussell/frontend/vite.config.ts\nLanguage: typescript\n\nimport tailwindcss from '@tailwindcss/vite';\nimport { sveltekit } from '@sveltejs/kit/vite';\nimport { defineConfig } from 'vite';\n", + "File: /home/popertots/Crussell/frontend/vite.config.ts\nLanguage: typescript\n\nexport default defineConfig({\n\tplugins: [tailwindcss(), sveltekit()]\n});", + "File: /home/popertots/Crussell/frontend/eslint.config.js\nLanguage: javascript\n\nimport prettier from 'eslint-config-prettier';\nimport { fileURLToPath } from 'node:url';\nimport { includeIgnoreFile } from '@eslint/compat';\nimport js from '@eslint/js';\nimport svelte from 'eslint-plugin-svelte';\nimport { defineConfig } from 'eslint/config';\nimport globals from 'globals';\nimport ts from 'typescript-eslint';\nimport svelteConfig from './svelte.config.js';\n", + "File: /home/popertots/Crussell/frontend/eslint.config.js\nLanguage: javascript\n\nconst gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));\n", + "File: /home/popertots/Crussell/frontend/eslint.config.js\nLanguage: javascript\n\nexport default defineConfig(\n\tincludeIgnoreFile(gitignorePath),\n\tjs.configs.recommended,\n\t...ts.configs.recommended,\n\t...svelte.configs.recommended,\n\tprettier,\n\t...svelte.configs.prettier,", + "File: /home/popertots/Crussell/frontend/eslint.config.js\nLanguage: javascript\n\n\t{", + "File: /home/popertots/Crussell/frontend/eslint.config.js\nLanguage: javascript\n\n\t\tlanguageOptions: {\n\t\t\tglobals: { ...globals.browser, ...globals.node }\n\t\t},", + "File: /home/popertots/Crussell/frontend/eslint.config.js\nLanguage: javascript\n\n\t\trules: {", + "File: /home/popertots/Crussell/frontend/eslint.config.js\nLanguage: javascript\n\n\t\t\t// typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.", + "File: /home/popertots/Crussell/frontend/eslint.config.js\nLanguage: javascript\n\n\t\t\t// see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors\n\t\t\t'no-undef': 'off'\n\t\t}\n\t},", + "File: /home/popertots/Crussell/frontend/eslint.config.js\nLanguage: javascript\n\n\t{\n\t\tfiles: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],", + "File: /home/popertots/Crussell/frontend/eslint.config.js\nLanguage: javascript\n\n\t\tlanguageOptions: {", + "File: /home/popertots/Crussell/frontend/eslint.config.js\nLanguage: javascript\n\n\t\t\tparserOptions: {\n\t\t\t\tprojectService: true,\n\t\t\t\textraFileExtensions: ['.svelte'],\n\t\t\t\tparser: ts.parser,\n\t\t\t\tsvelteConfig\n\t\t\t}\n\t\t}\n\t}\n);", + "File: /home/popertots/Crussell/frontend/svelte.config.js\nLanguage: javascript\n\nimport adapter from '@sveltejs/adapter-static';\nimport { vitePreprocess } from '@sveltejs/vite-plugin-svelte';\n\n/** @type {import('@sveltejs/kit').Config} */", + "File: /home/popertots/Crussell/frontend/svelte.config.js\nLanguage: javascript\n\nconst config = {", + "File: /home/popertots/Crussell/frontend/svelte.config.js\nLanguage: javascript\n\n\t// Consult https://svelte.dev/docs/kit/integrations", + "File: /home/popertots/Crussell/frontend/svelte.config.js\nLanguage: javascript\n\n\t// for more information about preprocessors\n\tpreprocess: vitePreprocess(),\n", + "File: /home/popertots/Crussell/frontend/svelte.config.js\nLanguage: javascript\n\n\tkit: {", + "File: /home/popertots/Crussell/frontend/svelte.config.js\nLanguage: javascript\n\n\t\t// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.", + "File: /home/popertots/Crussell/frontend/svelte.config.js\nLanguage: javascript\n\n\t\t// If your environment is not supported, or you settled on a specific environment, switch out the adapter.", + "File: /home/popertots/Crussell/frontend/svelte.config.js\nLanguage: javascript\n\n\t\t// See https://svelte.dev/docs/kit/adapters for more information about adapters.", + "File: /home/popertots/Crussell/frontend/svelte.config.js\nLanguage: javascript\n\n\t\tadapter: adapter({\n\t\t\tpages: 'build',\n\t\t\tassets: 'build',\n\t\t\tfallback: 'index.html'\n\t\t}),", + "File: /home/popertots/Crussell/frontend/svelte.config.js\nLanguage: javascript\n\n\t\talias: {\n\t\t\t$components: './src/components'\n\t\t}\n\t}\n};\n", + "File: /home/popertots/Crussell/frontend/svelte.config.js\nLanguage: javascript\n\nexport default config;", + "File: /home/popertots/Crussell/frontend/src/app.d.ts\nLanguage: typescript\n\n// See https://svelte.dev/docs/kit/types#app.d.ts", + "File: /home/popertots/Crussell/frontend/src/app.d.ts\nLanguage: typescript\n\n// for information about these interfaces", + "File: /home/popertots/Crussell/frontend/src/app.d.ts\nLanguage: typescript\n\ndeclare global {", + "File: /home/popertots/Crussell/frontend/src/app.d.ts\nLanguage: typescript\n\n\tnamespace App {", + "File: /home/popertots/Crussell/frontend/src/app.d.ts\nLanguage: typescript\n\n\t\t// interface Error {}", + "File: /home/popertots/Crussell/frontend/src/app.d.ts\nLanguage: typescript\n\n\t\t// interface Locals {}", + "File: /home/popertots/Crussell/frontend/src/app.d.ts\nLanguage: typescript\n\n\t\t// interface PageData {}", + "File: /home/popertots/Crussell/frontend/src/app.d.ts\nLanguage: typescript\n\n\t\t// interface PageState {}", + "File: /home/popertots/Crussell/frontend/src/app.d.ts\nLanguage: typescript\n\n\t\t// interface Platform {}\n\t}\n}\n", + "File: /home/popertots/Crussell/frontend/src/app.d.ts\nLanguage: typescript\n\nexport { };\ndeclare module 'swiper/svelte';", + "File: /home/popertots/Crussell/frontend/src/lib/utils.ts\nLanguage: typescript\n\nimport { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/utils.ts\nLanguage: typescript\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n", + "File: /home/popertots/Crussell/frontend/src/lib/utils.ts\nLanguage: typescript\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any", + "File: /home/popertots/Crussell/frontend/src/lib/utils.ts\nLanguage: typescript\n\nexport type WithoutChild = T extends { child?: any } ? Omit : T;", + "File: /home/popertots/Crussell/frontend/src/lib/utils.ts\nLanguage: typescript\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any", + "File: /home/popertots/Crussell/frontend/src/lib/utils.ts\nLanguage: typescript\n\nexport type WithoutChildren = T extends { children?: any } ? Omit : T;", + "File: /home/popertots/Crussell/frontend/src/lib/utils.ts\nLanguage: typescript\n\nexport type WithoutChildrenOrChild = WithoutChildren>;", + "File: /home/popertots/Crussell/frontend/src/lib/utils.ts\nLanguage: typescript\n\nexport type WithElementRef = T & { ref?: U | null };", + "File: /home/popertots/Crussell/frontend/src/lib/index.ts\nLanguage: typescript\n\n// place files you want to import through the `$lib` alias in this folder.", + "File: /home/popertots/Crussell/frontend/src/routes/+page.svelte\nType: markup\n
\n\t{#if authStore.isLoading}\n\t\t\n\t\t
\n\t\t\t\n\t\t
\n\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t{:else}\n\t\t

\n\t\t\t{#if !authStore.isAuthenticated}\n\t\t\t\tWelcome to Crussell Nails\n\t\t\t{:else if authStore.currentUser?.role === 'unverified_email'}\n\t\t\t\tWelcome {authStore.currentUser?.firstName}\n\t\t\t{:else}\n\t\t\t\tWelcome back {authStore.currentUser?.firstName}\n\t\t\t{/if}\n\t\t

\n\t\t

\n\t\t\tProfessional beauty treatments in a calm and friendly environment.\n\t\t

\n\t\t\n\t{/if}\n
\n\n
\n\t

Our Services

\n\t
\n\t\t
\n\t\t\t

Hands

\n\t\t\t

\n\t\t\t\tLorem hand ipsum nail dolor art sit paint amet, skin consectetur cuticle adipiscing file\n\t\t\t\telit.\n\t\t\t

\n\t\t
\n\t\t
\n\t\t\t

Feet

\n\t\t\t

\n\t\t\t\tLorem foot ipsum nail dolor art sit paint amet, skin consectetur cuticle adipiscing file\n\t\t\t\telit.\n\t\t\t

\n\t\t
\n\t\t
\n\t\t\t

Brows

\n\t\t\t

\n\t\t\t\tLorem brow ipsum eye dolor art sit shape amet, tint consectetur colour adipiscing tweaze\n\t\t\t\telit.\n\t\t\t

\n\t\t
\n\t\t
\n\t\t\t

Wax

\n\t\t\t

\n\t\t\t\tLorem wax ipsum leg dolor body sit sticky amet, back consectetur sack adipiscing crack elit.\n\t\t\t

\n\t\t
\n\t
\n
\n\n\n\n
\n\t
\n\t\t

Why Choose Us?

\n\t\t

\n\t\t\tLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus\n\t\t\ttortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices\n\t\t\tdiam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci\n\t\t\tnec nonummy molestie,\n\t\t

\n\t
\n
\n\n
\n\t

Ready to Treat Yourself?

\n\t\n
", + "File: /home/popertots/Crussell/frontend/src/routes/+error.svelte\nType: markup\n
\n\t

{status}

\n\t

\n\t\t{status === 404\n\t\t\t? \"Oops! The page you are looking for doesn't exist.\"\n\t\t\t: 'Something went wrong. Please try again later.'}\n\t

\n\n\t{#if error?.message}\n\t\t

{error.message}

\n\t{/if}\n\n\t\n
", + "File: /home/popertots/Crussell/frontend/src/routes/+layout.svelte\nType: function\nName: updateToasterPosition\nDoc: \nCode:\n\tfunction updateToasterPosition() {\n\t\t// Use 640px (Tailwind's 'sm' breakpoint) to differentiate desktop and mobile\n\t\tif (typeof window !== 'undefined' && window.innerWidth >= 768) {\n\t\t\ttoasterPosition = 'bottom-center';\n\t\t} else {\n\t\t\ttoasterPosition = 'top-center';\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/+layout.svelte\nType: function\nName: verify_email\nDoc: \nCode:\n\tasync function verify_email() {\n\t\talert('Verifying email address...');\n\t\tconst response = await fetch('/api/verify-email', {\n\t\t\tmethod: 'POST'\n\t\t});\n\n\t\tif (response.ok) {\n\t\t\twindow.location.reload();\n\t\t} else {\n\t\t\talert('Failed to verify email address');\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/+layout.svelte\nType: markup\n\n\t\n\n\n\n\n\n
\n\t{#if authStore.currentUser?.role === 'unverified_email'}\n\t\t
\n\t\t\tPlease verify your email address to continue. Didn't recieve the email? Check your spam\n\t\t\tfolder, or \n\t\t\t\tclick here\n\t\t\t\n\t\t\tto resend it.\n\t\t
\n\t{/if}\n\t{@render children?.()}\n
", + "File: /home/popertots/Crussell/frontend/src/routes/contact/+page.svelte\nType: markup\n
\n\t

Contact Me

\n\t\n
", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: type\nName: Service\nDoc: \nCode:\n\ttype Service = {\n\t\tid: string;\n\t\tname: string;\n\t\tdescription: string;\n\t\tprice: number;\n\t\tduration_minutes: number;\n\t\tpatch_test_duration_hours: number;\n\t\tminimum_age_required: number;\n\t};\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: fetchServices\nDoc: \nCode:\n\tasync function fetchServices() {\n\t\tservicesLoading = true;\n\t\ttry {\n\t\t\tconst response = await fetch('/api/services', {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t// Optional: If your user endpoint requires auth\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst data: Service[] = await response.json();\n\t\t\t\tservices = data;\n\t\t\t} else {\n\t\t\t\tconsole.error('Failed to fetch services:', response.status);\n\t\t\t\ttoast.error('Failed to load services');\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error fetching services:', err);\n\t\t\ttoast.error('Network error loading services');\n\t\t} finally {\n\t\t\tservicesLoading = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: fetchHoursForMonth\nDoc: \nCode:\n\tasync function fetchHoursForMonth(date: CalendarDate) {\n\t\tconst monthKey = `${date.year}-${String(date.month).padStart(2, '0')}`;\n\n\t\t// Use cached data if available\n\t\tif (workingHoursCache.has(monthKey) && availableHoursCache.has(monthKey)) {\n\t\t\tsetTimeout(() => {\n\t\t\t\tworkingHours = workingHoursCache.get(monthKey)!;\n\t\t\t\tavailableHours = availableHoursCache.get(monthKey)!;\n\t\t\t}, 0);\n\t\t\treturn;\n\t\t}\n\n\t\tloadingWorkingHours = true;\n\t\tloadingAvailableHours = true;\n\n\t\ttry {\n\t\t\t// Calculate start and end of month\n\t\t\tconst startOfMonth = new CalendarDate(date.year, date.month, 1);\n\t\t\tconst endOfMonth = new CalendarDate(\n\t\t\t\tdate.year,\n\t\t\t\tdate.month,\n\t\t\t\tdate.calendar.getDaysInMonth(date)\n\t\t\t);\n\n\t\t\tconst startStr = startOfMonth.toString();\n\t\t\tconst endStr = endOfMonth.toString();\n\n\t\t\t// Fetch working hours\n\t\t\tconst workingHoursResponse = await fetch(\n\t\t\t\t`/api/scheduling/working-hours?start=${startStr}&end=${endStr}`\n\t\t\t);\n\t\t\tif (!workingHoursResponse.ok) {\n\t\t\t\tthrow new Error(`HTTP error! status: ${workingHoursResponse.status}`);\n\t\t\t}\n\n\t\t\ttype WorkingHoursDay = {\n\t\t\t\tdate: string;\n\t\t\t\tweekday: number;\n\t\t\t\tstartTime: string;\n\t\t\t\tendTime: string;\n\t\t\t\tisOpen: boolean;\n\t\t\t\tsource: string;\n\t\t\t};\n\n\t\t\tconst workingHoursData: Array = await workingHoursResponse.json();\n\t\t\tconst workingHoursMap: Record<\n\t\t\t\tstring,\n\t\t\t\t{ isOpen: boolean; startTime: string; endTime: string }\n\t\t\t> = {};\n\n\t\t\tworkingHoursData.forEach((day) => {\n\t\t\t\tworkingHoursMap[day.date] = {\n\t\t\t\t\tisOpen: day.isOpen,\n\t\t\t\t\tstartTime: day.startTime,\n\t\t\t\t\tendTime: day.endTime\n\t\t\t\t};\n\t\t\t});\n\n\t\t\tworkingHoursCache.set(monthKey, workingHoursMap);\n\t\t\tworkingHours = workingHoursMap;\n\n\t\t\t// Fetch available hours\n\t\t\tconst availableHoursResponse = await fetch(\n\t\t\t\t`/api/scheduling/available-hours?start=${startStr}&end=${endStr}`\n\t\t\t);\n\t\t\tif (!availableHoursResponse.ok) {\n\t\t\t\tthrow new Error(`HTTP error! status: ${availableHoursResponse.status}`);\n\t\t\t}\n\n\t\t\ttype AvailableHoursDay = {\n\t\t\t\tdate: string;\n\t\t\t\tweekday: number;\n\t\t\t\tisOpen: boolean;\n\t\t\t\tslots: Array<{ startTime: string; endTime: string }>;\n\t\t\t\tsource: string;\n\t\t\t};\n\n\t\t\tconst availableHoursData: Array = await availableHoursResponse.json();\n\t\t\tconst availableHoursMap: Record<\n\t\t\t\tstring,\n\t\t\t\t{ isOpen: boolean; slots: Array<{ startTime: string; endTime: string }> }\n\t\t\t> = {};\n\n\t\t\tavailableHoursData.forEach((day) => {\n\t\t\t\tavailableHoursMap[day.date] = {\n\t\t\t\t\tisOpen: day.isOpen,\n\t\t\t\t\tslots: day.slots\n\t\t\t\t};\n\t\t\t});\n\n\t\t\tavailableHoursCache.set(monthKey, availableHoursMap);\n\t\t\tavailableHours = availableHoursMap;\n\n\t\t\t// Set default selected date if not set\n\t\t\tif (!selectedDate) {\n\t\t\t\tsetDefaultSelectedDate(workingHoursMap);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error('Failed to fetch hours:', error);\n\t\t\t// Fallback to current date if API fails\n\t\t\tif (!selectedDate) {\n\t\t\t\tselectedDate = minDate;\n\t\t\t}\n\t\t} finally {\n\t\t\tloadingWorkingHours = false;\n\t\t\tloadingAvailableHours = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: type\nName: WorkingHoursDay\nDoc: \nCode:\n\t\t\ttype WorkingHoursDay = {\n\t\t\t\tdate: string;\n\t\t\t\tweekday: number;\n\t\t\t\tstartTime: string;\n\t\t\t\tendTime: string;\n\t\t\t\tisOpen: boolean;\n\t\t\t\tsource: string;\n\t\t\t};\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: type\nName: AvailableHoursDay\nDoc: \nCode:\n\t\t\ttype AvailableHoursDay = {\n\t\t\t\tdate: string;\n\t\t\t\tweekday: number;\n\t\t\t\tisOpen: boolean;\n\t\t\t\tslots: Array<{ startTime: string; endTime: string }>;\n\t\t\t\tsource: string;\n\t\t\t};\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: setDefaultSelectedDate\nDoc: \nCode:\n\tfunction setDefaultSelectedDate(\n\t\thoursMap: Record\n\t) {\n\t\tconst currentDate = new Date();\n\t\tlet nextDate = new Date(currentDate);\n\n\t\tfor (let i = 1; i < 30; i++) {\n\t\t\tnextDate = new Date(currentDate);\n\t\t\tnextDate.setDate(currentDate.getDate() + i);\n\t\t\tconst dateStr = nextDate.toISOString().split('T')[0];\n\n\t\t\tif (hoursMap[dateStr]?.isOpen) {\n\t\t\t\tselectedDate = new CalendarDate(\n\t\t\t\t\tnextDate.getFullYear(),\n\t\t\t\t\tnextDate.getMonth() + 1,\n\t\t\t\t\tnextDate.getDate()\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!selectedDate) {\n\t\t\tconst tomorrow = new Date();\n\t\t\ttomorrow.setDate(tomorrow.getDate() + 1);\n\t\t\tselectedDate = new CalendarDate(\n\t\t\t\ttomorrow.getFullYear(),\n\t\t\t\ttomorrow.getMonth() + 1,\n\t\t\t\ttomorrow.getDate()\n\t\t\t);\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: calculateEndTime\nDoc: \nCode:\n\tfunction calculateEndTime(startTime: string, durationMinutes: number): string {\n\t\tconst [hours, minutes] = startTime.split(':').map(Number);\n\t\tconst date = new Date();\n\t\tdate.setHours(hours, minutes, 0, 0);\n\t\tdate.setMinutes(date.getMinutes() + durationMinutes);\n\t\tconst endHours = date.getHours().toString().padStart(2, '0');\n\t\tconst endMinutes = date.getMinutes().toString().padStart(2, '0');\n\t\treturn `${endHours}:${endMinutes}`;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: formatTime\nDoc: \nCode:\n\tfunction formatTime(time: string): string {\n\t\tconst parts = time.split(':').map(Number);\n\t\tconst hours = parts[0];\n\t\tconst minutes = parts.length > 1 ? parts[1] : 0;\n\n\t\tif (hours === 12 && minutes === 0) {\n\t\t\treturn 'Noon';\n\t\t} else if (hours === 0 && minutes === 0) {\n\t\t\treturn 'Midnight';\n\t\t}\n\n\t\tconst period = hours >= 12 ? 'PM' : 'AM';\n\t\tconst displayHours = hours % 12 || 12;\n\t\treturn `${displayHours}:${minutes.toString().padStart(2, '0')} ${period}`;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: isDateUnavailable\nDoc: \nCode:\n\tfunction isDateUnavailable(date: DateValue): boolean {\n\t\tif (!(date instanceof CalendarDate)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Check if date is outside allowed range\n\t\tif (date.compare(minDate) < 0 || date.compare(maxCalendarDate) > 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Check if we have working hours data\n\t\tif (!workingHours) return true; // Changed from false to true when data not loaded\n\n\t\tconst dateStr = date.toString();\n\t\tconst dayHours = workingHours[dateStr];\n\n\t\t// If no data for this date, assume unavailable\n\t\tif (!dayHours) return true;\n\n\t\t// If closed, mark as unavailable\n\t\tif (!dayHours.isOpen) return true;\n\n\t\t// Check if there are any available time slots for the selected services\n\t\tif (selectedServices.length > 0) {\n\t\t\tconst duration = getTotalDuration();\n\t\t\tconst availableSlots = generateAvailableTimeSlots(duration, date);\n\t\t\t// If no slots available for the required duration, mark as unavailable\n\t\t\tif (availableSlots.length === 0) return true;\n\t\t}\n\n\t\treturn false;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: generateGroupedTimeSlots\nDoc: \nCode:\n\tfunction generateGroupedTimeSlots(\n\t\tduration: number,\n\t\tdate: CalendarDate | undefined\n\t): Array<{\n\t\ttype: 'available' | 'unavailable';\n\t\tstartTime: string;\n\t\tendTime: string;\n\t\tisGrouped?: boolean;\n\t}> {\n\t\tif (!date || !workingHours) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst dateStr = date.toString();\n\t\tconst dayWorkingHours = workingHours[dateStr];\n\n\t\tif (!dayWorkingHours || !dayWorkingHours.isOpen) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst groupedSlots: Array<{\n\t\t\ttype: 'available' | 'unavailable';\n\t\t\tstartTime: string;\n\t\t\tendTime: string;\n\t\t\tisGrouped?: boolean;\n\t\t}> = [];\n\t\tconst [startHour, startMinute] = dayWorkingHours.startTime.split(':').map(Number);\n\t\tconst [endHour, endMinute] = dayWorkingHours.endTime.split(':').map(Number);\n\n\t\tlet startTotalMinutes = startHour * 60 + startMinute;\n\t\tconst endTotalMinutes = endHour * 60 + endMinute;\n\n\t\tconst now = new Date();\n\t\tconst today = new CalendarDate(now.getFullYear(), now.getMonth() + 1, now.getDate());\n\t\tconst isToday = date.compare(today) === 0;\n\n\t\t// Apply 2-hour buffer for today's appointments\n\t\tif (isToday) {\n\t\t\tconst currentMinutes = now.getHours() * 60 + now.getMinutes();\n\t\t\tconst minimumStartMinutes = currentMinutes + 120;\n\t\t\tstartTotalMinutes = Math.max(startTotalMinutes, minimumStartMinutes);\n\t\t}\n\n\t\t// Get available time slots that fit our duration\n\t\tconst availableSlots = generateAvailableTimeSlots(duration, date);\n\n\t\tlet currentUnavailableStart: string | null = null;\n\n\t\t// Generate all 15-minute increments within working hours\n\t\tfor (let minutes = startTotalMinutes; minutes < endTotalMinutes; minutes += 15) {\n\t\t\tconst hour = Math.floor(minutes / 60);\n\t\t\tconst minute = minutes % 60;\n\t\t\tconst timeStr = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;\n\n\t\t\t// Check if this time slot is available (fits duration and within available hours)\n\t\t\tconst isAvailable = availableSlots.includes(timeStr);\n\n\t\t\tif (isAvailable) {\n\t\t\t\t// If we were building an unavailable group, push it first\n\t\t\t\tif (currentUnavailableStart !== null) {\n\t\t\t\t\tconst groupEndTime = calculatePreviousTime(timeStr); // End before this available slot\n\t\t\t\t\tgroupedSlots.push({\n\t\t\t\t\t\ttype: 'unavailable',\n\t\t\t\t\t\tstartTime: currentUnavailableStart,\n\t\t\t\t\t\tendTime: groupEndTime,\n\t\t\t\t\t\tisGrouped: true\n\t\t\t\t\t});\n\t\t\t\t\tcurrentUnavailableStart = null;\n\t\t\t\t}\n\n\t\t\t\t// Add available slot\n\t\t\t\tconst slotEndTime = calculateEndTime(timeStr, duration);\n\t\t\t\tgroupedSlots.push({\n\t\t\t\t\ttype: 'available',\n\t\t\t\t\tstartTime: timeStr,\n\t\t\t\t\tendTime: slotEndTime\n\t\t\t\t});\n\n\t\t\t\t// If this available slot ends at or after closing time, break out early\n\t\t\t\t// to avoid generating unnecessary slots\n\t\t\t\tif (timeToMinutes(slotEndTime) >= endTotalMinutes) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Start or continue unavailable group\n\t\t\t\tif (currentUnavailableStart === null) {\n\t\t\t\t\tcurrentUnavailableStart = timeStr;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Close any remaining unavailable group at the end of the day\n\t\t// BUT only if there are no available slots that already reach closing time\n\t\tif (currentUnavailableStart !== null) {\n\t\t\tconst lastAvailableSlot = groupedSlots.filter((s) => s.type === 'available').pop();\n\t\t\tconst lastAvailableEnd = lastAvailableSlot ? timeToMinutes(lastAvailableSlot.endTime) : 0;\n\n\t\t\t// Only add the final unavailable group if:\n\t\t\t// 1. It actually contains some time before closing\n\t\t\t// 2. No available slot already reaches closing time\n\t\t\tconst unavailableStartMinutes = timeToMinutes(currentUnavailableStart);\n\n\t\t\tif (unavailableStartMinutes < endTotalMinutes && lastAvailableEnd < endTotalMinutes) {\n\t\t\t\tgroupedSlots.push({\n\t\t\t\t\ttype: 'unavailable',\n\t\t\t\t\tstartTime: currentUnavailableStart,\n\t\t\t\t\tendTime: dayWorkingHours.endTime,\n\t\t\t\t\tisGrouped: true\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn groupedSlots;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: timeToMinutes\nDoc: \nCode:\n\tfunction timeToMinutes(time: string): number {\n\t\tconst [hours, minutes] = time.split(':').map(Number);\n\t\treturn hours * 60 + minutes;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: calculatePreviousTime\nDoc: \nCode:\n\tfunction calculatePreviousTime(time: string): string {\n\t\tconst [hours, minutes] = time.split(':').map(Number);\n\t\tlet totalMinutes = hours * 60 + minutes;\n\t\ttotalMinutes -= 15; // Go back 15 minutes\n\n\t\tconst prevHours = Math.floor(totalMinutes / 60);\n\t\tconst prevMinutes = totalMinutes % 60;\n\t\treturn `${String(prevHours).padStart(2, '0')}:${String(prevMinutes).padStart(2, '0')}`;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: generateAvailableTimeSlots\nDoc: \nCode:\n\tfunction generateAvailableTimeSlots(duration: number, date: CalendarDate | undefined): string[] {\n\t\tif (!date || !workingHours || !availableHours) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst dateStr = date.toString();\n\t\tconst dayWorkingHours = workingHours[dateStr];\n\t\tconst dayAvailableHours = availableHours[dateStr];\n\n\t\t// Add check for slots existence\n\t\tif (\n\t\t\t!dayWorkingHours ||\n\t\t\t!dayWorkingHours.isOpen ||\n\t\t\t!dayAvailableHours ||\n\t\t\t!dayAvailableHours.slots\n\t\t) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst slots: string[] = [];\n\t\tconst now = new Date();\n\t\tconst today = new CalendarDate(now.getFullYear(), now.getMonth() + 1, now.getDate());\n\t\tconst isToday = date.compare(today) === 0;\n\n\t\tfor (const slot of dayAvailableHours.slots) {\n\t\t\tconst [startHour, startMinute] = slot.startTime.split(':').map(Number);\n\t\t\tconst [endHour, endMinute] = slot.endTime.split(':').map(Number);\n\n\t\t\tlet startTotalMinutes = startHour * 60 + startMinute;\n\t\t\tconst endTotalMinutes = endHour * 60 + endMinute;\n\n\t\t\t// Apply 2-hour buffer for today's appointments\n\t\t\tif (isToday) {\n\t\t\t\tconst currentMinutes = now.getHours() * 60 + now.getMinutes();\n\t\t\t\tconst minimumStartMinutes = currentMinutes + 120;\n\t\t\t\tstartTotalMinutes = Math.max(startTotalMinutes, minimumStartMinutes);\n\t\t\t}\n\n\t\t\t// Generate 15-minute increments within this available slot\n\t\t\tfor (let minutes = startTotalMinutes; minutes < endTotalMinutes; minutes += 15) {\n\t\t\t\tconst slotEndMinutes = minutes + duration;\n\n\t\t\t\t// Check if the full duration fits within the available slot\n\t\t\t\tif (slotEndMinutes <= endTotalMinutes) {\n\t\t\t\t\tconst hour = Math.floor(minutes / 60);\n\t\t\t\t\tconst minute = minutes % 60;\n\t\t\t\t\tconst timeStr = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;\n\t\t\t\t\tslots.push(timeStr);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn slots;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: getTotalDuration\nDoc: \nCode:\n\tfunction getTotalDuration() {\n\t\treturn selectedServices.reduce(\n\t\t\t(total, service: Service) => total + service.duration_minutes,\n\t\t\t0\n\t\t);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: getTotalPrice\nDoc: \nCode:\n\tfunction getTotalPrice() {\n\t\treturn selectedServices.reduce((total, service: Service) => total + service.price, 0);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: toggleService\nDoc: \nCode:\n\tfunction toggleService(service: any) {\n\t\tconst index = selectedServices.findIndex((s) => s.id === service.id);\n\t\tconst wasSelected = index >= 0;\n\n\t\tif (wasSelected) {\n\t\t\tselectedServices = selectedServices.filter((s) => s.id !== service.id);\n\t\t} else {\n\t\t\tselectedServices = [...selectedServices, service];\n\t\t}\n\n\t\t// Clear selected date and time when services change, as they may no longer be valid\n\t\tselectedTime = null;\n\t\tselectedDate = undefined;\n\n\t\t// Re-fetch available hours when services change and we're on step 2\n\t\tif (currentStep === 2) {\n\t\t\t// Clear the cache to force refetch with new duration\n\t\t\tavailableHoursCache.clear();\n\t\t\tif (selectedDate) {\n\t\t\t\tfetchHoursForMonth(selectedDate);\n\t\t\t}\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: isServiceSelected\nDoc: \nCode:\n\tfunction isServiceSelected(service: any) {\n\t\treturn selectedServices.some((s) => s.id === service.id);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: generateAllTimeSlots\nDoc: \nCode:\n\tfunction generateAllTimeSlots(\n\t\tduration: number,\n\t\tdate: CalendarDate | undefined\n\t): Array<{ time: string; type: 'available' | 'unavailable' }> {\n\t\tif (!date || !workingHours) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst dateStr = date.toString();\n\t\tconst dayWorkingHours = workingHours[dateStr];\n\n\t\tif (!dayWorkingHours || !dayWorkingHours.isOpen) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst allSlots: Array<{ time: string; type: 'available' | 'unavailable' }> = [];\n\t\tconst [startHour, startMinute] = dayWorkingHours.startTime.split(':').map(Number);\n\t\tconst [endHour, endMinute] = dayWorkingHours.endTime.split(':').map(Number);\n\n\t\tlet startTotalMinutes = startHour * 60 + startMinute;\n\t\tconst endTotalMinutes = endHour * 60 + endMinute;\n\n\t\tconst now = new Date();\n\t\tconst today = new CalendarDate(now.getFullYear(), now.getMonth() + 1, now.getDate());\n\t\tconst isToday = date.compare(today) === 0;\n\n\t\t// Apply 2-hour buffer for today's appointments\n\t\tif (isToday) {\n\t\t\tconst currentMinutes = now.getHours() * 60 + now.getMinutes();\n\t\t\tconst minimumStartMinutes = currentMinutes + 120;\n\t\t\tstartTotalMinutes = Math.max(startTotalMinutes, minimumStartMinutes);\n\t\t}\n\n\t\t// Get available time slots that fit our duration\n\t\tconst availableSlots = generateAvailableTimeSlots(duration, date);\n\n\t\t// Generate all 15-minute increments within working hours\n\t\tfor (let minutes = startTotalMinutes; minutes < endTotalMinutes; minutes += 15) {\n\t\t\tconst hour = Math.floor(minutes / 60);\n\t\t\tconst minute = minutes % 60;\n\t\t\tconst timeStr = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;\n\n\t\t\t// Check if this time slot is available (fits duration and within available hours)\n\t\t\tconst isAvailable = availableSlots.includes(timeStr);\n\n\t\t\tallSlots.push({\n\t\t\t\ttime: timeStr,\n\t\t\t\ttype: isAvailable ? 'available' : 'unavailable'\n\t\t\t});\n\t\t}\n\n\t\treturn allSlots;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: formatDuration\nDoc: \nCode:\n\tfunction formatDuration(minutes: number): string {\n\t\tconst hours = Math.floor(minutes / 60);\n\t\tconst remainingMinutes = minutes % 60;\n\n\t\tif (hours === 0) {\n\t\t\treturn `${remainingMinutes} minutes`;\n\t\t} else if (remainingMinutes === 0) {\n\t\t\treturn `${hours} ${hours === 1 ? 'hour' : 'hours'}`;\n\t\t} else {\n\t\t\treturn `${hours} ${hours === 1 ? 'hour' : 'hours'} ${remainingMinutes} minutes`;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: getDayWithOrdinal\nDoc: \nCode:\n\tfunction getDayWithOrdinal(date: CalendarDate): string {\n\t\tconst monthName = new Date(date.year, date.month - 1, date.day).toLocaleDateString('en-GB', {\n\t\t\tmonth: 'long'\n\t\t});\n\t\tconst day = date.day;\n\t\tif (day > 3 && day < 21) return monthName + ' ' + day + 'th';\n\t\tswitch (day % 10) {\n\t\t\tcase 1:\n\t\t\t\treturn monthName + ' ' + day + 'st';\n\t\t\tcase 2:\n\t\t\t\treturn monthName + ' ' + day + 'nd';\n\t\t\tcase 3:\n\t\t\t\treturn monthName + ' ' + day + 'rd';\n\t\t\tdefault:\n\t\t\t\treturn monthName + ' ' + day + 'th';\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: nextStep\nDoc: \nCode:\n\tfunction nextStep() {\n\t\tif (currentStep < 4) {\n\t\t\tcurrentStep++;\n\t\t\tsetTimeout(() => {\n\t\t\t\twindow.scrollTo({ top: 0, behavior: 'smooth' });\n\t\t\t}, 50);\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: prevStep\nDoc: \nCode:\n\tfunction prevStep() {\n\t\tif (currentStep > 1) {\n\t\t\tcurrentStep--;\n\t\t\tsetTimeout(() => {\n\t\t\t\twindow.scrollTo({ top: 0, behavior: 'smooth' });\n\t\t\t}, 50);\n\n\t\t\t// Re-fetch available hours when returning to step 2\n\t\t\tif (currentStep === 2 && selectedDate) {\n\t\t\t\tconst monthKey = `${selectedDate.year}-${String(selectedDate.month).padStart(2, '0')}`;\n\t\t\t\tavailableHoursCache.delete(monthKey); // Force refetch\n\t\t\t\tfetchHoursForMonth(selectedDate);\n\t\t\t}\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: function\nName: handleBooking\nDoc: \nCode:\n\tfunction handleBooking() {\n\t\talert('Booking submitted! (This is just a prototype - no backend connected yet)');\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/book/+page.svelte\nType: markup\n
\n\t
\n\t\t

Book Your Appointment

\n\t\t

Professional beauty treatments in a calm and friendly environment

\n\t
\n\n\t\n\t
\n\t\t{#each ['Service', 'Date & Time', 'Details', 'Payment'] as step, index}\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{index + 1}\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{step}\n\t\t\t\t\n\t\t\t\t{#if index < 3}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t
\n\t\t{/each}\n\t\n\n\t\n\t{#if currentStep === 1}\n\t\t\n\t\t\t\n\t\t\t\tChoose Your Services\n\t\t\t\tSelect one or more treatments for your appointment\n\t\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t{#if servicesLoading}\n\t\t\t\t\t\t

Loading services...

\n\t\t\t\t\t{:else if services.length === 0}\n\t\t\t\t\t\t

No services available at the moment.

\n\t\t\t\t\t{:else}\n\t\t\t\t\t\t{#each services as service}\n\t\t\t\t\t\t\t toggleService(service)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t

{service.name}

\n\t\t\t\t\t\t\t\t\t\t

{service.description}

\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{service.duration_minutes} mins\n\t\t\t\t\t\t\t\t\t\t\t\u00a3{service.price}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{#if isServiceSelected(service)}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t{/if}\n\t\t\t\t\n\n\t\t\t\t{#if selectedServices.length > 0}\n\t\t\t\t\t
\n\t\t\t\t\t\t

Selected Services

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#each selectedServices as service}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{service.name}\n\t\t\t\t\t\t\t\t\t{service.duration_minutes} mins \u2022 \u00a3{service.price}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\tEstimated Duration:\n\t\t\t\t\t\t\t\t{formattedTotalDuration}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\tTotal Cost:\n\t\t\t\t\t\t\t\t\u00a3{getTotalPrice()}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t
\n\t{/if}\n\n\t\n\t{#if currentStep === 2}\n\t\t\n\t\t\t\n\t\t\t\tChoose Date & Time\n\t\t\t\t\n\t\t\t\t\t{selectedServices.map((s) => s.name).join(', ')} \u2022 {formattedTotalDuration} total \u2022 \u00a3{getTotalPrice()}\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#if (loadingWorkingHours || loadingAvailableHours) && selectedDate}\n\t\t\t\t\t\t\t\t
Loading available times...
\n\t\t\t\t\t\t\t{:else if groupedTimeSlots.length > 0}\n\t\t\t\t\t\t\t\t{#if selectedDate}\n\t\t\t\t\t\t\t\t\t
{getDayWithOrdinal(selectedDate)}
\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{#each groupedTimeSlots as slot (slot.startTime)}\n\t\t\t\t\t\t\t\t\t\t{#if slot.type === 'available'}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\t\tselectedTime = slot.startTime;\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tclass={`w-full hover:bg-fuchsia-50 ${\n\t\t\t\t\t\t\t\t\t\t\t\t\tslot.startTime === selectedTime ? 'bg-fuchsia-100' : ''\n\t\t\t\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{formatTime(slot.startTime)}\n\t\t\t\t\t\t\t\t\t\t\t\t- {formatTime(slot.endTime)}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{formatTime(slot.startTime)}\n\t\t\t\t\t\t\t\t\t\t\t\t- {formatTime(slot.endTime)}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{:else if selectedServices.length === 0}\n\t\t\t\t\t\t\t\t

Select services first

\n\t\t\t\t\t\t\t{:else if !selectedDate}\n\t\t\t\t\t\t\t\t

Select a date first

\n\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t

No available slots

\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t{#if selectedDate && selectedTime}\n\t\t\t\t\tAppointment for\n\t\t\t\t\t\n\t\t\t\t\t\t{selectedDate.toDate(getLocalTimeZone()).toLocaleDateString('en-US', {\n\t\t\t\t\t\t\tweekday: 'long',\n\t\t\t\t\t\t\tday: 'numeric',\n\t\t\t\t\t\t\tmonth: 'short'\n\t\t\t\t\t\t})}\n\t\t\t\t\t\n\t\t\t\t\t
at {formatTime(selectedTime)}\n\t\t\t\t{:else}\n\t\t\t\t\tSelect a date and time\n\t\t\t\t{/if}\n\t\t\t
\n\n\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t{#if selectedDate && selectedTime}\n\t\t\t\t\t\t\tAppointment for\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{selectedDate.toDate(getLocalTimeZone()).toLocaleDateString('en-US', {\n\t\t\t\t\t\t\t\t\tweekday: 'long',\n\t\t\t\t\t\t\t\t\tday: 'numeric',\n\t\t\t\t\t\t\t\t\tmonth: 'short'\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tat {formatTime(selectedTime)}\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\tSelect a date and time\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t{/if}\n\n\t\n\t{#if currentStep === 3}\n\t\t\n\t\t\t\n\t\t\t\tYour Details\n\t\t\t\tPlease provide your contact information\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t

Booking Summary

\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\tServices:\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{#each selectedServices as service}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{service.name}\n\t\t\t\t\t\t\t\t\t\t\u00a3{service.price}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\tDate:\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{selectedDate?.toDate(getLocalTimeZone()).toLocaleDateString('en-US', {\n\t\t\t\t\t\t\t\t\tweekday: 'long',\n\t\t\t\t\t\t\t\t\tday: 'numeric',\n\t\t\t\t\t\t\t\t\tmonth: 'long'\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\tTime:\n\t\t\t\t\t\t\t{selectedTime}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\tEstimated Duration:\n\t\t\t\t\t\t\t{formattedTotalDuration}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\tTotal Cost:\n\t\t\t\t\t\t\t\u00a3{getTotalPrice()}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t

* Required fields

\n\t\t\t\t\t

\n\t\t\t\t\t\tBy booking, you agree to our Terms & Conditions and Privacy Policy. We'll send you\n\t\t\t\t\t\tappointment reminders via email and SMS.\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\tNext: Payment\n\t\t\t\t\n\t\t\t\n\t\t
\n\t{/if}\n\n\t{#if currentStep === 4}\n\t\t\n\t\t\n\t\t
\n\t\t\t

Payment Confirmation

\n\t\t\t

\n\t\t\t\tLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus\n\t\t\t\ttortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices\n\t\t\t\tdiam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci\n\t\t\t\tnec nonummy molestie,\n\t\t\t

\n\t\t
\n\t{/if}\n", + "File: /home/popertots/Crussell/frontend/src/routes/login/+page.svelte\nType: function\nName: toggleMode\nDoc: \nCode:\n\tfunction toggleMode() {\n\t\tisLogin = !isLogin;\n\t\tagreedToPolicy = false;\n\t\tformData = {\n\t\t\temail: '',\n\t\t\tpassword: '',\n\t\t\tfirstName: '',\n\t\t\tlastName: '',\n\t\t\tconfirmPassword: '',\n\t\t\tphone: '',\n\t\t\tdateOfBirth: ''\n\t\t};\n\t\tvalidationErrors = {\n\t\t\temail: '',\n\t\t\tphone: '',\n\t\t\tdateOfBirth: ''\n\t\t};\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/login/+page.svelte\nType: function\nName: validateEmail\nDoc: \nCode:\n\tfunction validateEmail(email: string): boolean {\n\t\t// ASCII-only email regex (safe with most providers)\n\t\tconst asciiRegex = /^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$/;\n\n\t\t// Unicode-friendly regex (valid per RFC 6531)\n\t\tconst unicodeRegex = /^[\\p{L}\\p{M}0-9._%+\\-]+@[\\p{L}\\p{M}0-9.\\-]+\\.[\\p{L}\\p{M}]{2,}$/u;\n\n\t\tif (!email) {\n\t\t\tvalidationErrors.email = '';\n\t\t\treturn true;\n\t\t}\n\n\t\tif (asciiRegex.test(email)) {\n\t\t\t// OK: standard ASCII address\n\t\t\tvalidationErrors.email = '';\n\t\t\treturn true;\n\t\t} else if (unicodeRegex.test(email)) {\n\t\t\t// Looks like a valid internationalised address, but unsupported\n\t\t\tvalidationErrors.email =\n\t\t\t\t'This looks like a valid international email address, ' +\n\t\t\t\t'but our providers only supports standard (A-Z, 0-9) email addresses. ' +\n\t\t\t\t'We are sorry. Please use an ASCII-compatible email.';\n\t\t\treturn false;\n\t\t} else {\n\t\t\t// Not even valid under the Unicode spec\n\t\t\tvalidationErrors.email = 'Invalid email format';\n\t\t\treturn false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/login/+page.svelte\nType: function\nName: validatePhone\nDoc: \nCode:\n\tfunction validatePhone(phone: string): boolean {\n\t\tif (!phone) {\n\t\t\tvalidationErrors.phone = '';\n\t\t\treturn true;\n\t\t}\n\n\t\t// Remove spaces, hyphens, parentheses for validation\n\t\tconst cleanPhone = phone.replace(/[\\s\\-()]/g, '');\n\n\t\t// UK phone regex: +44 followed by 10-11 digits, or 0 followed by 10-11 digits\n\t\tconst phoneRegex = /^(\\+44[1-9]\\d{9,10}|0[1-9]\\d{9,10})$/;\n\t\tconst isValid = phoneRegex.test(cleanPhone);\n\t\tvalidationErrors.phone = isValid ? '' : 'Invalid UK phone number';\n\t\treturn isValid;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/login/+page.svelte\nType: function\nName: formatPhoneInput\nDoc: \nCode:\n\tfunction formatPhoneInput(value: string): string {\n\t\t// Remove all non-digit and non-plus characters\n\t\tlet cleaned = value.replace(/[^\\d+]/g, '');\n\n\t\t// If it starts with +44, format as +44 XXXX XXXXXX\n\t\tif (cleaned.startsWith('+44')) {\n\t\t\tconst digits = cleaned.slice(3);\n\t\t\tif (digits.length <= 4) return `+44 ${digits}`;\n\t\t\tif (digits.length <= 10) return `+44 ${digits.slice(0, 4)} ${digits.slice(4)}`;\n\t\t\treturn `+44 ${digits.slice(0, 4)} ${digits.slice(4, 10)}`;\n\t\t}\n\n\t\t// If it starts with 0, format as 0XXXX XXXXXX\n\t\tif (cleaned.startsWith('0')) {\n\t\t\tif (cleaned.length <= 5) return cleaned;\n\t\t\treturn `${cleaned.slice(0, 5)} ${cleaned.slice(5, 11)}`;\n\t\t}\n\n\t\treturn cleaned;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/login/+page.svelte\nType: function\nName: handlePhoneInput\nDoc: \nCode:\n\tfunction handlePhoneInput(e: Event) {\n\t\tconst target = e.target as HTMLInputElement;\n\t\tconst cursorPos = target.selectionStart || 0;\n\t\tconst oldLength = formData.phone.length;\n\n\t\tformData.phone = formatPhoneInput(target.value);\n\n\t\t// Adjust cursor position after formatting\n\t\tconst newLength = formData.phone.length;\n\t\tconst diff = newLength - oldLength;\n\n\t\trequestAnimationFrame(() => {\n\t\t\ttarget.setSelectionRange(cursorPos + diff, cursorPos + diff);\n\t\t});\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/login/+page.svelte\nType: function\nName: validateAge\nDoc: \nCode:\n\tfunction validateAge(dateStr: string): boolean {\n\t\tif (!dateStr) {\n\t\t\tvalidationErrors.dateOfBirth = '';\n\t\t\treturn true;\n\t\t}\n\n\t\tconst dob = new Date(dateStr);\n\t\tconst today = new Date();\n\t\tconst sixteenYearsAgo = new Date(today.getFullYear() - 16, today.getMonth(), today.getDate());\n\n\t\tconst isValid = dob <= sixteenYearsAgo;\n\t\tvalidationErrors.dateOfBirth = isValid\n\t\t\t? ''\n\t\t\t: \"You must be at least 16 years old to register, but you can call for an appointment by clicking 'Contact' at the top of the page.\";\n\t\treturn isValid;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/login/+page.svelte\nType: function\nName: normalizeFormData\nDoc: \nCode:\n\tfunction normalizeFormData() {\n\t\treturn {\n\t\t\tfirstName: formData.firstName.trim(),\n\t\t\tlastName: formData.lastName.trim(),\n\t\t\temail: formData.email.trim().toLowerCase(),\n\t\t\tpassword: formData.password,\n\t\t\tphone: formData.phone.replace(/[\\s\\-()]/g, ''), // Remove formatting\n\t\t\tdateOfBirth: formData.dateOfBirth.trim(),\n\t\t\tagreedToPolicy: agreedToPolicy\n\t\t};\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/login/+page.svelte\nType: function\nName: handleSubmit\nDoc: \nCode:\n\tasync function handleSubmit() {\n\t\tif (isLogin) {\n\t\t\t// Login flow with loading toast\n\t\t\tconst loadingToast = toast.loading('Signing in...');\n\n\t\t\ttry {\n\t\t\t\tconst response = await fetch('/api/login', {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\temail: formData.email.trim().toLowerCase(),\n\t\t\t\t\t\tpassword: formData.password\n\t\t\t\t\t})\n\t\t\t\t});\n\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json();\n\t\t\t\t\tlocalStorage.setItem('authToken', data.token);\n\t\t\t\t\ttoast.success('Successfully logged in!', { id: loadingToast });\n\t\t\t\t\twindow.location.href = '/';\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\ttoast.error('Login already in progress. Please wait.', { id: loadingToast });\n\t\t\t\t} else if (response.status === 401) {\n\t\t\t\t\ttoast.error('Invalid email or password.', { id: loadingToast });\n\t\t\t\t} else {\n\t\t\t\t\tconst text = await response.text();\n\t\t\t\t\ttoast.error('Error: ' + text, { id: loadingToast });\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(err);\n\t\t\t\ttoast.error('Network error, please try again later.', { id: loadingToast });\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Validate before submitting\n\t\tconst isEmailValid = validateEmail(formData.email);\n\t\tconst isPhoneValid = validatePhone(formData.phone);\n\t\tconst isAgeValid = validateAge(formData.dateOfBirth);\n\n\t\tif (!isEmailValid || !isPhoneValid || !isAgeValid) {\n\t\t\ttoast.error('Please fix the validation errors before submitting.');\n\t\t\treturn;\n\t\t}\n\n\t\t// Registration flow with loading toast\n\t\tconst loadingToast = toast.loading('Creating your account...');\n\n\t\ttry {\n\t\t\tconst response = await fetch('/api/register', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tbody: JSON.stringify(normalizeFormData())\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\ttoast.success('Account created successfully!', { id: loadingToast });\n\t\t\t\ttoggleMode();\n\t\t\t} else {\n\t\t\t\tconst text = await response.text();\n\t\t\t\ttoast.error('Error: ' + text, { id: loadingToast });\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error(err);\n\t\t\ttoast.error('Network error, please try again later.', { id: loadingToast });\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/login/+page.svelte\nType: function\nName: handleSocialLogin\nDoc: \nCode:\n\tfunction handleSocialLogin(provider: string) {\n\t\talert(`${provider} login clicked! (This is just a prototype)`);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/login/+page.svelte\nType: markup\n
\n\t
\n\t\t\n\t\t
\n\t\t\t

\n\t\t\t\t{isLogin ? 'Welcome back' : 'Create account'}\n\t\t\t

\n\t\t\t

\n\t\t\t\t{isLogin\n\t\t\t\t\t? 'Sign in to your account to continue'\n\t\t\t\t\t: 'Choose an option below to create your account'}\n\t\t\t

\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t\n\n\t\t\t\n\n\t\t\t\n\t\t
\n\n\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t\tor continue with email\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t {\n\t\t\t\te.preventDefault();\n\t\t\t\thandleSubmit();\n\t\t\t}}\n\t\t\tclass=\"space-y-4\"\n\t\t>\n\t\t\t{#if !isLogin}\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t (formData.firstName = formData.firstName.trim())}\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t (formData.lastName = formData.lastName.trim())}\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t validatePhone(formData.phone)}\n\t\t\t\t\t\trequired\n\t\t\t\t\t/>\n\t\t\t\t\t{#if validationErrors.phone}\n\t\t\t\t\t\t

{validationErrors.phone}

\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t validateAge(formData.dateOfBirth)}\n\t\t\t\t\t\tmax={new Date(new Date().setFullYear(new Date().getFullYear() - 16))\n\t\t\t\t\t\t\t.toISOString()\n\t\t\t\t\t\t\t.split('T')[0]}\n\t\t\t\t\t\trequired\n\t\t\t\t\t/>\n\t\t\t\t\t{#if validationErrors.dateOfBirth}\n\t\t\t\t\t\t

{validationErrors.dateOfBirth}

\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t{/if}\n\n\t\t\t
\n\t\t\t\t\n\t\t\t\t validateEmail(formData.email)}\n\t\t\t\t\trequired\n\t\t\t\t/>\n\t\t\t\t{#if validationErrors.email}\n\t\t\t\t\t

{validationErrors.email}

\n\t\t\t\t{/if}\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\n\t\t\t\t{#if !isLogin && passwordStrength}\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{passwordStrength.feedback.warning\n\t\t\t\t\t\t\t\t? passwordStrength.feedback.warning\n\t\t\t\t\t\t\t\t: `Strength: ${['Very weak', 'Weak', 'Fair', 'Strong', 'Very strong'][passwordStrength.score]}`}\n\t\t\t\t\t\t

\n\t\t\t\t\t\t{#if passwordStrength.feedback.suggestions.length > 0}\n\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t{#each passwordStrength.feedback.suggestions as suggestion}\n\t\t\t\t\t\t\t\t\t
  • {suggestion}
  • \n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t
\n\n\t\t\t{#if !isLogin}\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{#if formData.confirmPassword && formData.password !== formData.confirmPassword}\n\t\t\t\t\t\t

Passwords do not match

\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/if}\n\n\t\t\t\n\t\t\n\n\t\t
\n\t\t\t{isLogin ? \"Don't have an account?\" : 'Already have an account?'}\n\t\t\t\n\t\t
\n\t
\n", + "File: /home/popertots/Crussell/frontend/src/routes/prices/+page.svelte\nType: type\nName: Service\nDoc: \nCode:\n\ttype Service = {\n\t\tid: string;\n\t\tname: string;\n\t\tdescription: string;\n\t\tprice: number;\n\t\tduration_minutes: number;\n\t\tpatch_test_duration_hours: number;\n\t\tminimum_age_required: number;\n\t};\n", + "File: /home/popertots/Crussell/frontend/src/routes/prices/+page.svelte\nType: function\nName: fetchServices\nDoc: \nCode:\n\tasync function fetchServices() {\n\t\tservicesLoading = true;\n\t\ttry {\n\t\t\tconst response = await fetch('/api/services', {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst data: Service[] = await response.json();\n\t\t\t\tservices = data;\n\t\t\t} else {\n\t\t\t\tconsole.error('Failed to fetch services:', response.status);\n\t\t\t\ttoast.error('Failed to load services');\n\t\t\t\t// Fallback to empty array\n\t\t\t\tservices = [];\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error fetching services:', err);\n\t\t\ttoast.error('Network error loading services');\n\t\t\t// Fallback to empty array\n\t\t\tservices = [];\n\t\t} finally {\n\t\t\tservicesLoading = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/prices/+page.svelte\nType: function\nName: formatDuration\nDoc: \nCode:\n\tfunction formatDuration(minutes: number): string {\n\t\tconst hours = Math.floor(minutes / 60);\n\t\tconst remainingMinutes = minutes % 60;\n\n\t\tif (hours === 0) {\n\t\t\treturn `${remainingMinutes} minutes`;\n\t\t} else if (remainingMinutes === 0) {\n\t\t\treturn `${hours} ${hours === 1 ? 'hour' : 'hours'}`;\n\t\t} else {\n\t\t\treturn `${hours} ${hours === 1 ? 'hour' : 'hours'} ${remainingMinutes} minutes`;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/prices/+page.svelte\nType: function\nName: formatPrice\nDoc: \nCode:\n\tfunction formatPrice(price: number): string {\n\t\treturn `\u00a3${price.toFixed(2)}`;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/prices/+page.svelte\nType: markup\n
\n\t
\n\t\t

Our Prices

\n\t\t

Professional beauty treatments with transparent pricing

\n\t
\n\n\t{#if servicesLoading}\n\t\t
\n\t\t\t
\n\t\t\t\t
Loading services...
\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t{:else if services.length === 0}\n\t\t
\n\t\t\t
No services available at the moment.
\n\t\t\t\n\t\t
\n\t{:else}\n\t\t
\n\t\t\t{#each services as service, index}\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

{service.name}

\n\t\t\t\t\t\t\t

{service.description}

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{formatDuration(service.duration_minutes)}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{#if service.patch_test_duration_hours > 0}\n\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\tPatch test required {service.patch_test_duration_hours}h before appointment\n\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{#if service.minimum_age_required > 0}\n\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\tMinimum age: {service.minimum_age_required} years\n\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
{formatPrice(service.price)}
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t{#if index < services.length - 1}\n\t\t\t\t\t\n\t\t\t\t{/if}\n\t\t\t{/each}\n\t\t\n\n\t\t\n\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t

Important Information:

\n\t\t\t\t\t
    \n\t\t\t\t\t\t
  • \n\t\t\t\t\t\t\t\u2022 Prices and durations are estimates and may vary based on individual requirements\n\t\t\t\t\t\t
  • \n\t\t\t\t\t\t
  • \u2022 Patch tests are required 24-48 hours before certain treatments
  • \n\t\t\t\t\t\t
  • \u2022 24 hours notice required for cancellations
  • \n\t\t\t\t\t\t
  • \u2022 Payment is due at the time of service
  • \n\t\t\t\t\t\t
  • \u2022 Deposit required for new and guest accounts
  • \n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t\n\n\t\t\n\t\t\n\t\t\t

Ready to Book?

\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\tGet in Touch\n\t\t\t\t\n\t\t\t
\n\t\t\n\t{/if}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: handleFilesDropped\nDoc: \nCode:\n\tfunction handleFilesDropped(files: File[]) {\n\t\tuploadFiles = files;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: uploadOneOrMany\nDoc: \nCode:\n\tasync function uploadOneOrMany() {\n\t\tif (!uploadFiles.length) return;\n\t\tuploading = true;\n\t\tuploadResults = [];\n\t\tuploadProgress = 0;\n\n\t\tfor (let i = 0; i < uploadFiles.length; i++) {\n\t\t\tconst file = uploadFiles[i];\n\t\t\tconst fd = new FormData();\n\t\t\tfd.append('file', file);\n\n\t\t\ttry {\n\t\t\t\t// Note: API call is mocked here, replace with your actual endpoint\n\t\t\t\t// Mock success/fail\n\t\t\t\tawait new Promise((r) => setTimeout(r, 500)); // Simulate network delay\n\t\t\t\tif (file.name.toLowerCase().includes('fail')) {\n\t\t\t\t\tuploadResults.push({ name: file.name, error: 'Mocked API error' });\n\t\t\t\t} else {\n\t\t\t\t\tuploadResults.push({ name: file.name, url: `/images/${file.name}` });\n\t\t\t\t}\n\t\t\t} catch (err: any) {\n\t\t\t\tuploadResults.push({ name: file.name, error: err?.message || 'Network error' });\n\t\t\t}\n\n\t\t\tuploadProgress = Math.round(((i + 1) / uploadFiles.length) * 100);\n\t\t}\n\n\t\tuploading = false;\n\t\tuploadFiles = [];\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: type\nName: WorkingHourRow\nDoc: \nCode:\n\ttype WorkingHourRow = {\n\t\tid?: number;\n\t\tweekday: number;\n\t\tstart_time: string;\n\t\tend_time: string;\n\t\tis_open: boolean;\n\t};\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: formatTime\nDoc: \nCode:\n\tfunction formatTime(time: string): string {\n\t\tconst [hours, minutes] = time.split(':').map(Number);\n\n\t\t// Special case for 12:00\n\t\tif (hours === 12 && minutes === 0) {\n\t\t\treturn 'Noon';\n\t\t} else if (hours === 0 && minutes === 0) {\n\t\t\treturn 'Midnight';\n\t\t}\n\n\t\tconst period = hours >= 12 ? 'PM' : 'AM';\n\t\tconst displayHours = hours % 12 || 12;\n\t\treturn `${displayHours}:${minutes.toString().padStart(2, '0')} ${period}`;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: timeToInputValue\nDoc: \nCode:\n\tfunction timeToInputValue(time: string): string {\n\t\t// Handle special cases\n\t\tif (time === 'Noon') return '12:00';\n\t\tif (time === 'Midnight') return '00:00';\n\n\t\t// Parse 12-hour format\n\t\tconst match = time.match(/^(\\d{1,2}):(\\d{2})\\s*(AM|PM)$/i);\n\t\tif (!match) return time; // Return as-is if not in expected format\n\n\t\tlet hours = parseInt(match[1]);\n\t\tconst minutes = match[2];\n\t\tconst period = match[3].toUpperCase();\n\n\t\tif (period === 'PM' && hours !== 12) hours += 12;\n\t\tif (period === 'AM' && hours === 12) hours = 0;\n\n\t\treturn `${hours.toString().padStart(2, '0')}:${minutes}`;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: fetchDefaultHours\nDoc: \nCode:\n\tasync function fetchDefaultHours() {\n\t\tif (pageState !== 'authorized') return;\n\n\t\tdefaultHoursIsLoading = true;\n\t\tlet error = null;\n\n\t\ttry {\n\t\t\tconst response = await fetch('/api/scheduling/default-hours', {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst data = await response.json();\n\t\t\t\tdefaultHours = data.map((hour: any) => ({\n\t\t\t\t\tweekday: hour.weekday,\n\t\t\t\t\tstart_time: formatTime(hour.startTime),\n\t\t\t\t\tend_time: formatTime(hour.endTime),\n\t\t\t\t\tis_open: hour.isOpen\n\t\t\t\t}));\n\t\t\t} else {\n\t\t\t\tconst text = await response.text();\n\t\t\t\terror = 'Failed to load working hours: ' + text;\n\t\t\t\tconsole.error('Error fetching default hours:', text);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\terror = 'Network error: ' + (err instanceof Error ? err.message : 'Unknown error');\n\t\t\tconsole.error('Error fetching default hours:', err);\n\t\t} finally {\n\t\t\tif (error) toast.error(error);\n\t\t\tdefaultHoursIsLoading = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: type\nName: ExceptionGroup\nDoc: \nCode:\n\ttype ExceptionGroup = {\n\t\tid?: number;\n\t\tname: string;\n\t\tdescription: string;\n\t\tweekStarts: string[];\n\t\thours: WorkingHourRow[];\n\t};\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: fetchExceptionGroups\nDoc: \nCode:\n\tasync function fetchExceptionGroups() {\n\t\tif (pageState !== 'authorized') return;\n\n\t\texceptionGroupsLoading = true;\n\t\ttry {\n\t\t\tconst response = await fetch('/api/scheduling/exceptional-groups', {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst data = await response.json();\n\t\t\t\tif (data === null || data.length === 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\texceptionGroups = data.map((group: any) => ({\n\t\t\t\t\tid: group.id,\n\t\t\t\t\tname: group.name,\n\t\t\t\t\tdescription: group.description,\n\t\t\t\t\tweekStarts: group.weekStarts || [],\n\t\t\t\t\thours:\n\t\t\t\t\t\tgroup.hours?.map((h: any) => ({\n\t\t\t\t\t\t\tid: h.id,\n\t\t\t\t\t\t\tweekday: h.weekday,\n\t\t\t\t\t\t\tstart_time: formatTime(h.startTime),\n\t\t\t\t\t\t\tend_time: formatTime(h.endTime),\n\t\t\t\t\t\t\tis_open: h.isOpen\n\t\t\t\t\t\t})) || []\n\t\t\t\t}));\n\t\t\t} else {\n\t\t\t\tconsole.error('Failed to fetch exception groups:', response.status);\n\t\t\t\ttoast.error('Failed to load exception groups');\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error fetching exception groups:', err);\n\t\t\ttoast.error('Network error loading exception groups');\n\t\t} finally {\n\t\t\texceptionGroupsLoading = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: prepareDefaultHoursEdit\nDoc: \nCode:\n\tfunction prepareDefaultHoursEdit() {\n\t\t// Deep copy the current default hours into the draft state\n\t\tdefaultHoursDraft = JSON.parse(JSON.stringify(defaultHours));\n\t\t// Convert display format back to input format\n\t\tdefaultHoursDraft = defaultHoursDraft.map((row) => ({\n\t\t\t...row,\n\t\t\tstart_time: timeToInputValue(row.start_time),\n\t\t\tend_time: timeToInputValue(row.end_time)\n\t\t}));\n\t\tshowDefaultHoursModal = true;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: confirmSaveDefaultHours\nDoc: \nCode:\n\tasync function confirmSaveDefaultHours() {\n\t\tsavingHours = true;\n\t\tconst loadingToast = toast.loading('Saving default hours...');\n\n\t\ttry {\n\t\t\t// Map snake_case to camelCase for API\n\t\t\tconst payload = defaultHoursDraft.map((hour) => ({\n\t\t\t\tweekday: hour.weekday,\n\t\t\t\tstartTime: hour.start_time,\n\t\t\t\tendTime: hour.end_time,\n\t\t\t\tisOpen: hour.is_open\n\t\t\t}));\n\n\t\t\tconst response = await fetch('/api/scheduling/default-hours', {\n\t\t\t\tmethod: 'PUT',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(payload)\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\t// Update the main state from the draft state if successful\n\t\t\t\tdefaultHours = JSON.parse(JSON.stringify(defaultHoursDraft));\n\t\t\t\tshowDefaultHoursModal = false;\n\t\t\t\tshowSaveDefaultHoursAlert = false;\n\t\t\t\ttoast.success('Default hours saved successfully!', { id: loadingToast });\n\t\t\t} else if (response.status === 401 || response.status === 403) {\n\t\t\t\ttoast.error('Unauthorized. Please log in again.', { id: loadingToast });\n\t\t\t} else {\n\t\t\t\tconst text = await response.text();\n\t\t\t\ttoast.error('Failed to save: ' + text, { id: loadingToast });\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('save default hours', err);\n\t\t\ttoast.error('Network error saving hours', { id: loadingToast });\n\t\t} finally {\n\t\t\tsavingHours = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: saveExceptionGroup\nDoc: \nCode:\n\tasync function saveExceptionGroup() {\n\t\t// Validate\n\t\tif (!exceptionDraft.name.trim()) {\n\t\t\ttoast.error('Please enter a group name');\n\t\t\treturn;\n\t\t}\n\n\t\tif (exceptionDraft.weekStarts.length === 0) {\n\t\t\ttoast.error('Please add at least one week');\n\t\t\treturn;\n\t\t}\n\n\t\tsavingHours = true;\n\t\tconst loadingToast = toast.loading('Creating exception group...');\n\n\t\ttry {\n\t\t\t// Map to API format\n\t\t\tconst payload = {\n\t\t\t\tname: exceptionDraft.name,\n\t\t\t\tdescription: exceptionDraft.description,\n\t\t\t\tweekStarts: exceptionDraft.weekStarts,\n\t\t\t\thours: exceptionDraft.hours.map((h) => ({\n\t\t\t\t\tweekday: h.weekday,\n\t\t\t\t\tstartTime: h.start_time,\n\t\t\t\t\tendTime: h.end_time,\n\t\t\t\t\tisOpen: h.is_open\n\t\t\t\t}))\n\t\t\t};\n\n\t\t\tconst response = await fetch('/api/scheduling/exceptional-groups', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(payload)\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\ttoast.success('Exception group created successfully!', { id: loadingToast });\n\t\t\t\tshowExceptionModal = false;\n\t\t\t\tresetExceptionForm();\n\t\t\t\tawait fetchExceptionGroups();\n\t\t\t} else {\n\t\t\t\tconst text = await response.text();\n\t\t\t\ttoast.error('Failed to create: ' + text, { id: loadingToast });\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error creating exception group:', err);\n\t\t\ttoast.error('Network error creating exception group', { id: loadingToast });\n\t\t} finally {\n\t\t\tsavingHours = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: confirmDeleteExceptionGroup\nDoc: \nCode:\n\tasync function confirmDeleteExceptionGroup() {\n\t\tif (exceptionToDelete === undefined) return;\n\n\t\tconst loadingToast = toast.loading('Deleting exception group...');\n\n\t\ttry {\n\t\t\tconst response = await fetch(`/api/scheduling/exceptional-groups?id=${exceptionToDelete}`, {\n\t\t\t\tmethod: 'DELETE',\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (response.ok || response.status === 204) {\n\t\t\t\ttoast.success('Exception group deleted successfully!', { id: loadingToast });\n\t\t\t\tshowDeleteExceptionAlert = false;\n\t\t\t\texceptionToDelete = undefined;\n\t\t\t\t// Refresh the exception groups list\n\t\t\t\tawait fetchExceptionGroups();\n\t\t\t} else {\n\t\t\t\tconst text = await response.text();\n\t\t\t\ttoast.error('Failed to delete: ' + text, { id: loadingToast });\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error deleting exception group:', err);\n\t\t\ttoast.error('Network error deleting exception group', { id: loadingToast });\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: openViewExceptionModal\nDoc: \nCode:\n\tfunction openViewExceptionModal(exception: ExceptionGroup) {\n\t\tviewingException = exception;\n\t\tshowViewExceptionModal = true;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: resetExceptionForm\nDoc: \nCode:\n\tfunction resetExceptionForm() {\n\t\texceptionDraft = {\n\t\t\tname: '',\n\t\t\tdescription: '',\n\t\t\tweekStarts: [],\n\t\t\thours: [\n\t\t\t\t{ weekday: 0, start_time: '00:00', end_time: '00:00', is_open: false },\n\t\t\t\t{ weekday: 1, start_time: '09:00', end_time: '17:00', is_open: true },\n\t\t\t\t{ weekday: 2, start_time: '09:00', end_time: '17:00', is_open: true },\n\t\t\t\t{ weekday: 3, start_time: '09:00', end_time: '17:00', is_open: true },\n\t\t\t\t{ weekday: 4, start_time: '12:00', end_time: '20:00', is_open: true },\n\t\t\t\t{ weekday: 5, start_time: '09:00', end_time: '17:00', is_open: true },\n\t\t\t\t{ weekday: 6, start_time: '00:00', end_time: '00:00', is_open: false }\n\t\t\t]\n\t\t};\n\t\tweekRangeFrom = '';\n\t\tweekRangeTo = '';\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: createNewException\nDoc: \nCode:\n\tfunction createNewException() {\n\t\tresetExceptionForm();\n\t\tshowExceptionModal = true;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: addWeekRange\nDoc: \nCode:\n\tfunction addWeekRange() {\n\t\tif (!weekRangeFrom || !weekRangeTo) {\n\t\t\ttoast.error('Please select both start and end dates');\n\t\t\treturn;\n\t\t}\n\n\t\taddWeeksToException(weekRangeFrom, weekRangeTo, exceptionDraft.weekStarts);\n\t\tweekRangeFrom = '';\n\t\tweekRangeTo = '';\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: removeWeek\nDoc: \nCode:\n\tfunction removeWeek(index: number) {\n\t\texceptionDraft.weekStarts = exceptionDraft.weekStarts.filter((_, i) => i !== index);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: type\nName: User\nDoc: \nCode:\n\ttype User = {\n\t\tid: string;\n\t\tn_first_name: string;\n\t\tn_last_name: string;\n\t\tfn?: string;\n\t\temail?: string;\n\t\tphone?: string;\n\t\tcreated_at?: string;\n\t\tprofile_pic_url?: string;\n\t\tloyalty_stamps?: number;\n\t};\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: type\nName: Booking\nDoc: \nCode:\n\ttype Booking = {\n\t\tid: string;\n\t\tstart_time: string; // ISO 8601\n\t\tstatus:\n\t\t\t| 'pending'\n\t\t\t| 'confirmed'\n\t\t\t| 'in_progress'\n\t\t\t| 'completed'\n\t\t\t| 'client_cancelled'\n\t\t\t| 'we_cancelled'\n\t\t\t| 're-schedule'\n\t\t\t| 'no_show';\n\t\tnotes?: string;\n\t\tcreated_at: string; // ISO 8601\n\t\tupdated_at: string; // ISO 8601\n\t\tcreated_by?: string;\n\n\t\t// Nested user object\n\t\tuser?: {\n\t\t\tid: string;\n\t\t\tfirst_name: string;\n\t\t\tlast_name: string;\n\t\t\tfull_name: string;\n\t\t\temail?: string;\n\t\t\tphone?: string;\n\t\t\tprofile_pic_url?: string;\n\t\t\tdate_of_birth?: string;\n\t\t\taccount_role: string;\n\t\t\tloyalty_stamps?: number;\n\t\t\treferral_code?: string;\n\t\t\treferral_code_uses?: number;\n\t\t\tcreated_at: string;\n\t\t\tnotes?: string;\n\t\t};\n\n\t\t// Services array - always present (backend ensures this)\n\t\tservices: Array<{\n\t\t\tbooking_id: string;\n\t\t\tservice_id: string;\n\t\t\toverride_price?: number;\n\t\t\toverride_duration_minutes?: number;\n\t\t\tservice_name?: string;\n\t\t\tservice_description?: string;\n\t\t\tprice?: number;\n\t\t\tduration_minutes?: number;\n\t\t}>;\n\n\t\t// Payments array\n\t\tpayments: Array<{\n\t\t\tid: string;\n\t\t\tbooking_id: string;\n\t\t\tpayment_type: 'deposit' | 'full' | 'tip' | 'balance' | 'partial';\n\t\t\tpayment_method: 'online_square' | 'in_person_card' | 'cash' | 'giftcard' | 'discount';\n\t\t\tvendor_code?: string;\n\t\t\tinvoice_number?: number;\n\t\t\tstatus: 'pending' | 'completed' | 'failed' | 'refunded';\n\t\t\tamount: number;\n\t\t\tis_vat_applicable: boolean;\n\t\t\tvat_rate?: number;\n\t\t\tvat_amount?: number;\n\t\t\tnet_amount?: number;\n\t\t\tcreated_at: string;\n\t\t\tupdated_at: string;\n\t\t\tcreated_by?: string;\n\t\t}>;\n\n\t\t// Computed/derived fields\n\t\ttotal_amount: number;\n\t\tamount_paid: number;\n\t\tamount_due: number;\n\t\tduration_minutes: number;\n\t};\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: fetchBookings\nDoc: \nCode:\n\tasync function fetchBookings() {\n\t\tif (pageState !== 'authorized') return;\n\t\tloadingSearch = true;\n\t\ttry {\n\t\t\tconst response = await fetch('/api/admin/bookings', {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (response.ok) {\n\t\t\t\tconst data = await response.json();\n\t\t\t\tconsole.log('Bookings API response:', data); // Debug log\n\n\t\t\t\tif (data.bookings && data.bookings.length === 0) {\n\t\t\t\t\tbookings = [];\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Map the response correctly - the backend returns the full Booking objects\n\t\t\t\tbookings = data.bookings.map((b: any) => ({\n\t\t\t\t\tid: b.id,\n\t\t\t\t\tstart_time: b.start_time,\n\t\t\t\t\tstatus: b.status,\n\t\t\t\t\tnotes: b.notes,\n\t\t\t\t\tcreated_at: b.created_at,\n\t\t\t\t\tupdated_at: b.updated_at,\n\t\t\t\t\tcreated_by: b.created_by,\n\t\t\t\t\t// User info is nested under user object\n\t\t\t\t\tuser: b.user\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tid: b.user.id,\n\t\t\t\t\t\t\t\tfull_name: b.user.full_name\n\t\t\t\t\t\t\t\t// Add other user fields if needed\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\t// Services array should be present (even if empty)\n\t\t\t\t\tservices: b.services || [],\n\t\t\t\t\t// Other computed fields from backend\n\t\t\t\t\ttotal_amount: b.total_amount || 0,\n\t\t\t\t\tamount_paid: b.amount_paid || 0,\n\t\t\t\t\tamount_due: b.amount_due || 0,\n\t\t\t\t\tduration_minutes: b.duration_minutes || 0\n\t\t\t\t}));\n\t\t\t\tconsole.log('Mapped bookings:', bookings); // Debug log\n\t\t\t} else {\n\t\t\t\tconst text = await response.text();\n\t\t\t\ttoast.error('Failed to load bookings: ' + text);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error fetching bookings:', err);\n\t\t\ttoast.error('Network error loading bookings');\n\t\t} finally {\n\t\t\tloadingSearch = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: searchBookings\nDoc: \nCode:\n\tasync function searchBookings() {\n\t\tif (pageState !== 'authorized') return;\n\t\tloadingSearch = true;\n\n\t\t// If no search query, use the regular get-all endpoint\n\t\tif (!bookingQuery.trim()) {\n\t\t\tawait fetchBookings();\n\t\t\tloadingSearch = false;\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(\n\t\t\t\t`/api/admin/bookings/search?q=${encodeURIComponent(bookingQuery)}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t\tif (response.ok) {\n\t\t\t\tconst data = await response.json();\n\t\t\t\tconsole.log('Search API response:', data); // Debug log\n\n\t\t\t\t// Map the search response correctly (same structure as fetchBookings)\n\t\t\t\tbookings = data.bookings.map((b: any) => ({\n\t\t\t\t\tid: b.id,\n\t\t\t\t\tstart_time: b.start_time,\n\t\t\t\t\tstatus: b.status,\n\t\t\t\t\tnotes: b.notes,\n\t\t\t\t\tcreated_at: b.created_at,\n\t\t\t\t\tupdated_at: b.updated_at,\n\t\t\t\t\tcreated_by: b.created_by,\n\t\t\t\t\tuser: b.user\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tid: b.user.id,\n\t\t\t\t\t\t\t\tfull_name: b.user.full_name\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tservices: b.services || [],\n\t\t\t\t\ttotal_amount: b.total_amount || 0,\n\t\t\t\t\tamount_paid: b.amount_paid || 0,\n\t\t\t\t\tamount_due: b.amount_due || 0,\n\t\t\t\t\tduration_minutes: b.duration_minutes || 0\n\t\t\t\t}));\n\t\t\t} else {\n\t\t\t\tconst text = await response.text();\n\t\t\t\ttoast.error('Failed to search bookings: ' + text);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error searching bookings:', err);\n\t\t\ttoast.error('Network error searching bookings');\n\t\t} finally {\n\t\t\tloadingSearch = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: openBookingModal\nDoc: \nCode:\n\tasync function openBookingModal(bookingId: string) {\n\t\tif (pageState !== 'authorized') return;\n\t\ttry {\n\t\t\tconst response = await fetch(`/api/admin/bookings/${bookingId}`, {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (response.ok) {\n\t\t\t\tconst data = await response.json();\n\t\t\t\tconsole.log('Booking details API response:', data); // Debug log\n\n\t\t\t\tselectedBooking = {\n\t\t\t\t\tid: data.id,\n\t\t\t\t\tstart_time: data.start_time,\n\t\t\t\t\tstatus: data.status,\n\t\t\t\t\tnotes: data.notes,\n\t\t\t\t\tuser: data.user\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tid: data.user.id,\n\t\t\t\t\t\t\t\tfirst_name: data.user.first_name,\n\t\t\t\t\t\t\t\tlast_name: data.user.last_name,\n\t\t\t\t\t\t\t\tfull_name: data.user.full_name,\n\t\t\t\t\t\t\t\temail: data.user.email,\n\t\t\t\t\t\t\t\tphone: data.user.phone,\n\t\t\t\t\t\t\t\tprofile_pic_url: data.user.profile_pic_url,\n\t\t\t\t\t\t\t\tdate_of_birth: data.user.date_of_birth,\n\t\t\t\t\t\t\t\taccount_role: data.user.account_role,\n\t\t\t\t\t\t\t\tloyalty_stamps: data.user.loyalty_stamps,\n\t\t\t\t\t\t\t\treferral_code: data.user.referral_code,\n\t\t\t\t\t\t\t\treferral_code_uses: data.user.referral_code_uses,\n\t\t\t\t\t\t\t\tcreated_at: data.user.created_at,\n\t\t\t\t\t\t\t\tnotes: data.user.notes\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tservices: (data.services || []).map((s: any) => ({\n\t\t\t\t\t\tbooking_id: s.booking_id,\n\t\t\t\t\t\tservice_id: s.service_id,\n\t\t\t\t\t\tservice_name: s.service_name,\n\t\t\t\t\t\tservice_description: s.service_description,\n\t\t\t\t\t\tprice: s.price,\n\t\t\t\t\t\tduration_minutes: s.duration_minutes\n\t\t\t\t\t})),\n\t\t\t\t\tpayments: (data.payments || []).map((p: any) => ({\n\t\t\t\t\t\tid: p.id,\n\t\t\t\t\t\tbooking_id: p.booking_id,\n\t\t\t\t\t\tpayment_type: p.payment_type,\n\t\t\t\t\t\tpayment_method: p.payment_method,\n\t\t\t\t\t\tvendor_code: p.vendor_code,\n\t\t\t\t\t\tinvoice_number: p.invoice_number,\n\t\t\t\t\t\tstatus: p.status,\n\t\t\t\t\t\tamount: p.amount,\n\t\t\t\t\t\tis_vat_applicable: p.is_vat_applicable,\n\t\t\t\t\t\tvat_rate: p.vat_rate,\n\t\t\t\t\t\tvat_amount: p.vat_amount,\n\t\t\t\t\t\tnet_amount: p.net_amount,\n\t\t\t\t\t\tcreated_at: p.created_at,\n\t\t\t\t\t\tupdated_at: p.updated_at,\n\t\t\t\t\t\tcreated_by: p.created_by\n\t\t\t\t\t})),\n\t\t\t\t\ttotal_amount: data.total_amount || 0,\n\t\t\t\t\tamount_paid: data.amount_paid || 0,\n\t\t\t\t\tamount_due: data.amount_due || 0,\n\t\t\t\t\tduration_minutes: data.duration_minutes || 0,\n\t\t\t\t\tcreated_at: data.created_at,\n\t\t\t\t\tupdated_at: data.updated_at,\n\t\t\t\t\tcreated_by: data.created_by\n\t\t\t\t};\n\t\t\t\tshowBookingModal = true;\n\t\t\t} else {\n\t\t\t\tconst text = await response.text();\n\t\t\t\ttoast.error('Failed to load booking details: ' + text);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error fetching booking details:', err);\n\t\t\ttoast.error('Network error loading booking details');\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: searchUsers\nDoc: \nCode:\n\tasync function searchUsers() {\n\t\tloadingSearch = true;\n\t\tawait new Promise((r) => setTimeout(r, 500));\n\t\tconst query = userQuery.toLowerCase();\n\t\tusers = [\n\t\t\t{\n\t\t\t\tid: 'user3',\n\t\t\t\tn_first_name: 'Test',\n\t\t\t\tn_last_name: 'Search',\n\t\t\t\temail: 'test@search.com',\n\t\t\t\tphone: '000',\n\t\t\t\tcreated_at: '2025-01-01T00:00:00Z',\n\t\t\t\tloyalty_stamps: 1\n\t\t\t},\n\t\t\t...users\n\t\t].filter(\n\t\t\t(u) =>\n\t\t\t\tu.n_first_name.toLowerCase().includes(query) ||\n\t\t\t\tu.n_last_name.toLowerCase().includes(query) ||\n\t\t\t\tu.email?.toLowerCase().includes(query) ||\n\t\t\t\tu.phone?.includes(query)\n\t\t);\n\t\tloadingSearch = false;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: openUserModal\nDoc: \nCode:\n\tasync function openUserModal(userId: string) {\n\t\tselectedUser = users.find((u) => u.id === userId) || null;\n\t\tif (!selectedUser) return;\n\n\t\t// Filter demo bookings for this user\n\t\tbookingUserHistory = bookings\n\t\t\t.filter((b) => b?.user?.id === userId)\n\t\t\t.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());\n\n\t\tshowUserModal = true;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: weekdayLabel\nDoc: \nCode:\n\tfunction weekdayLabel(i: number) {\n\t\treturn dayNames[i];\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: isoDateOf\nDoc: \nCode:\n\tfunction isoDateOf(d: Date) {\n\t\treturn d.toISOString().slice(0, 10);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: addWeeksToException\nDoc: \nCode:\n\tfunction addWeeksToException(fromISO: string, toISO: string, dest: string[]) {\n\t\tconst from = new Date(fromISO + 'T00:00:00');\n\t\tconst to = new Date(toISO + 'T00:00:00');\n\t\tconst first = new Date(from);\n\t\tconst day = first.getDay();\n\t\tconst daysToMonday = day === 0 ? -6 : 1 - day;\n\n\t\t// Set to the Monday of the current week\n\t\tfirst.setDate(first.getDate() + daysToMonday);\n\n\t\t// Add all Mondays in the range\n\t\tfor (let d = new Date(first); d <= to; d.setDate(d.getDate() + 7)) {\n\t\t\tdest.push(isoDateOf(new Date(d)));\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: type\nName: Service\nDoc: \nCode:\n\ttype Service = {\n\t\tid: string;\n\t\tname: string;\n\t\tdescription: string;\n\t\tprice: number;\n\t\tduration_minutes: number;\n\t\tis_active: boolean;\n\t\tpatch_test_duration_hours: number;\n\t\tminimum_age_required: number;\n\t\tcreated_at: string;\n\t\tupdated_at: string;\n\t\tcreated_by?: string;\n\t\tupdated_by?: string;\n\t};\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: fetchServices\nDoc: \nCode:\n\tasync function fetchServices() {\n\t\tif (pageState !== 'authorized') return;\n\n\t\tservicesLoading = true;\n\t\ttry {\n\t\t\tconst response = await fetch('/api/admin/services', {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst data = await response.json();\n\t\t\t\tservices = data;\n\t\t\t} else {\n\t\t\t\tconsole.error('Failed to fetch services:', response.status);\n\t\t\t\ttoast.error('Failed to load services');\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error fetching services:', err);\n\t\t\ttoast.error('Network error loading services');\n\t\t} finally {\n\t\t\tservicesLoading = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: toggleService\nDoc: \nCode:\n\tasync function toggleService(serviceId: string) {\n\t\tservicesUpdating[serviceId] = true;\n\t\ttry {\n\t\t\tconst response = await fetch(`/api/admin/services/${serviceId}/toggle`, {\n\t\t\t\tmethod: 'PUT',\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\ttoast.success('Service status updated');\n\t\t\t\t// Refresh the services list\n\t\t\t\tawait fetchServices();\n\t\t\t} else {\n\t\t\t\tconst errorText = await response.text();\n\t\t\t\ttoast.error(`Failed to update service: ${errorText}`);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error toggling service:', err);\n\t\t\ttoast.error('Network error updating service');\n\t\t} finally {\n\t\t\tservicesUpdating[serviceId] = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: deleteService\nDoc: \nCode:\n\tasync function deleteService(serviceId: string) {\n\t\tif (!confirm('Are you sure you want to delete this service? This action cannot be undone.')) {\n\t\t\treturn;\n\t\t}\n\n\t\tservicesUpdating[serviceId] = true;\n\n\t\ttry {\n\t\t\tconst response = await fetch(`/api/admin/services/${serviceId}`, {\n\t\t\t\tmethod: 'DELETE',\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\ttoast.success('Service deleted successfully');\n\t\t\t\t// Refresh the services list\n\t\t\t\tawait fetchServices();\n\t\t\t} else {\n\t\t\t\tconst errorText = await response.text();\n\t\t\t\ttoast.error(`Failed to delete service: ${errorText}`);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error deleting service:', err);\n\t\t\ttoast.error('Network error deleting service');\n\t\t} finally {\n\t\t\tservicesUpdating[serviceId] = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: validatePrice\nDoc: \nCode:\n\tfunction validatePrice(price: string): string {\n\t\t// First check if it's a valid number format (allows only digits and one decimal point)\n\t\tconst validFormat = /^\\d*\\.?\\d*$/.test(price);\n\t\tif (!validFormat) {\n\t\t\treturn 'Price must be a valid number (e.g., 4.50)';\n\t\t}\n\n\t\tconst numPrice = parseFloat(price);\n\t\tif (isNaN(numPrice)) {\n\t\t\treturn 'Price must be a valid number';\n\t\t}\n\n\t\tif (numPrice <= 0) {\n\t\t\treturn 'Price must be greater than 0';\n\t\t}\n\n\t\t// Check for exactly 0-2 decimal places\n\t\tconst decimalRegex = /^\\d+(\\.\\d{1,2})?$/;\n\t\tif (!decimalRegex.test(price)) {\n\t\t\treturn 'Price can have up to 2 decimal places';\n\t\t}\n\n\t\treturn '';\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: validateDuration\nDoc: \nCode:\n\tfunction validateDuration(value: number, field: string): string {\n\t\tif (isNaN(value)) {\n\t\t\treturn 'Must be a valid number';\n\t\t}\n\n\t\tif (!Number.isInteger(value)) {\n\t\t\treturn 'Must be a whole number';\n\t\t}\n\n\t\tif (field === 'duration_minutes' && value <= 0) {\n\t\t\treturn 'Duration must be greater than 0';\n\t\t}\n\n\t\tif (field === 'patch_test_duration_hours' && value < 0) {\n\t\t\treturn 'Cannot be negative';\n\t\t}\n\n\t\tif (field === 'minimum_age_required' && (value < 0 || value > 100)) {\n\t\t\treturn 'Must be between 0 and 100';\n\t\t}\n\n\t\treturn '';\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: validateName\nDoc: \nCode:\n\tfunction validateName(name: string): string {\n\t\tif (!name.trim()) {\n\t\t\treturn 'Service name is required';\n\t\t}\n\t\treturn '';\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: updateAllErrors\nDoc: \nCode:\n\tfunction updateAllErrors() {\n\t\tserviceErrors = {\n\t\t\tname: validateName(newService.name),\n\t\t\tprice: validatePrice(newService.price),\n\t\t\tduration_minutes: validateDuration(newService.duration_minutes, 'duration_minutes'),\n\t\t\tpatch_test_duration_hours: validateDuration(\n\t\t\t\tnewService.patch_test_duration_hours,\n\t\t\t\t'patch_test_duration_hours'\n\t\t\t),\n\t\t\tminimum_age_required: validateDuration(\n\t\t\t\tnewService.minimum_age_required,\n\t\t\t\t'minimum_age_required'\n\t\t\t)\n\t\t};\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: validateNameField\nDoc: \nCode:\n\tfunction validateNameField() {\n\t\tserviceErrors.name = validateName(newService.name);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: validatePriceField\nDoc: \nCode:\n\tfunction validatePriceField() {\n\t\tserviceErrors.price = validatePrice(newService.price);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: validateDurationField\nDoc: \nCode:\n\tfunction validateDurationField() {\n\t\tserviceErrors.duration_minutes = validateDuration(\n\t\t\tnewService.duration_minutes,\n\t\t\t'duration_minutes'\n\t\t);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: validatePatchTestField\nDoc: \nCode:\n\tfunction validatePatchTestField() {\n\t\tserviceErrors.patch_test_duration_hours = validateDuration(\n\t\t\tnewService.patch_test_duration_hours,\n\t\t\t'patch_test_duration_hours'\n\t\t);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: validateMinimumAgeField\nDoc: \nCode:\n\tfunction validateMinimumAgeField() {\n\t\tserviceErrors.minimum_age_required = validateDuration(\n\t\t\tnewService.minimum_age_required,\n\t\t\t'minimum_age_required'\n\t\t);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: createService\nDoc: \nCode:\n\tasync function createService() {\n\t\t// Update all errors before final validation\n\t\tupdateAllErrors();\n\n\t\t// Check if any errors exist\n\t\tconst hasErrors = Object.values(serviceErrors).some((error) => error !== '');\n\t\tif (hasErrors) {\n\t\t\ttoast.error('Please fix the validation errors before submitting');\n\t\t\treturn;\n\t\t}\n\n\t\t// Additional safety check with the derived property\n\t\tif (!isFormValid) {\n\t\t\ttoast.error('Form validation failed');\n\t\t\treturn;\n\t\t}\n\n\t\tcreatingService = true;\n\t\tconst loadingToast = toast.loading('Creating service...');\n\n\t\ttry {\n\t\t\tconst payload = {\n\t\t\t\tname: newService.name.trim(),\n\t\t\t\tdescription: newService.description.trim() || undefined,\n\t\t\t\tprice: parseFloat(newService.price),\n\t\t\t\tduration_minutes: newService.duration_minutes,\n\t\t\t\tpatch_test_duration_hours: newService.patch_test_duration_hours,\n\t\t\t\tminimum_age_required: newService.minimum_age_required\n\t\t\t};\n\n\t\t\tconst response = await fetch('/api/admin/services', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAuthorization: `Bearer ${authStore.currentToken}`\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(payload)\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst createdService = await response.json();\n\t\t\t\ttoast.success('Service created successfully!', { id: loadingToast });\n\n\t\t\t\t// Reset form and close modal\n\t\t\t\tresetServiceForm();\n\t\t\t\tshowServiceModal = false;\n\n\t\t\t\t// Refresh services list\n\t\t\t\tawait fetchServices();\n\t\t\t} else if (response.status === 409) {\n\t\t\t\ttoast.error('A service with this name already exists', { id: loadingToast });\n\t\t\t} else if (response.status === 400) {\n\t\t\t\tconst errorText = await response.text();\n\t\t\t\ttoast.error(`Validation error: ${errorText}`, { id: loadingToast });\n\t\t\t} else {\n\t\t\t\tconst errorText = await response.text();\n\t\t\t\ttoast.error(`Failed to create service: ${errorText}`, { id: loadingToast });\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('Error creating service:', err);\n\t\t\ttoast.error('Network error creating service', { id: loadingToast });\n\t\t} finally {\n\t\t\tcreatingService = false;\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: resetServiceForm\nDoc: \nCode:\n\tfunction resetServiceForm() {\n\t\tnewService = {\n\t\t\tname: '',\n\t\t\tdescription: '',\n\t\t\tprice: '',\n\t\t\tduration_minutes: 60,\n\t\t\tpatch_test_duration_hours: 0,\n\t\t\tminimum_age_required: 0\n\t\t};\n\t\tserviceErrors = {\n\t\t\tname: '',\n\t\t\tprice: '',\n\t\t\tduration_minutes: '',\n\t\t\tpatch_test_duration_hours: '',\n\t\t\tminimum_age_required: ''\n\t\t};\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: openServiceModal\nDoc: \nCode:\n\tfunction openServiceModal() {\n\t\tresetServiceForm();\n\t\tshowServiceModal = true;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: function\nName: calculateHours\nDoc: \nCode:\n\tfunction calculateHours(startTime: string, endTime: string): string {\n\t\t// Convert formatted times to 24-hour format for calculation\n\t\tconst start = timeToInputValue(startTime);\n\t\tconst end = timeToInputValue(endTime);\n\n\t\tconst [startHours, startMinutes] = start.split(':').map(Number);\n\t\tconst [endHours, endMinutes] = end.split(':').map(Number);\n\n\t\tconst startTotalMinutes = startHours * 60 + startMinutes;\n\t\tconst endTotalMinutes = endHours * 60 + endMinutes;\n\n\t\tconst diffMinutes = endTotalMinutes - startTotalMinutes;\n\t\tconst hours = Math.floor(diffMinutes / 60);\n\t\tconst minutes = diffMinutes % 60;\n\n\t\tif (minutes === 0) {\n\t\t\treturn `${hours}`;\n\t\t}\n\t\treturn `${hours}.${minutes === 30 ? '5' : Math.round((minutes / 60) * 10)}`;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/routes/admin/+page.svelte\nType: markup\n{#if pageState === 'loading'}\n\t\n\t
\n\t\t\n\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each Array(3) as _, i}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each Array(3) as _, i}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{#each Array(2) as _, i}\n\t\t\t\t\t\t\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each Array(7) as _, i}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each Array(3) as _, i}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t
\n{:else if pageState === 'authorized'}\n\t
\n\t\t
\n\t\t\t
\n\t\t\t\t

Admin Dashboard

\n\t\t\t\t

Manage portfolio images, working hours & user bookings

\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\tImage Upload\n\t\t\t\t\t\tUpload images for the portfolio or other uses.\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t

Drop files here, or click to open the file picker

\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t
Selected files ({uploadFiles.length})
\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each uploadFiles as f}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
{f.name} \u2022 {Math.round(f.size / 1024)}KB
\n\t\t\t\t\t\t\t\t (uploadFiles = uploadFiles.filter((x) => x !== f))}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tRemove\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t\t{#if uploadResults.length > 0}\n\t\t\t\t\t\t
Upload Results
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#each uploadResults as result}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{result.name}: {result.error\n\t\t\t\t\t\t\t\t\t\t? `Failed: ${result.error}`\n\t\t\t\t\t\t\t\t\t\t: `Success: ${result.url}`}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\n\t\t\n\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tUsers\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tSearch and manage user details.\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{users.length}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\tif ((e as KeyboardEvent).key === 'Enter') searchUsers();\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#each users as u}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
{u.fn || `${u.n_first_name} ${u.n_last_name}`}
\n\t\t\t\t\t\t\t\t\t\t
{u.email} \u2022 {u.phone}
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tBookings\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tSearch and manage booking history.\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{bookings.length}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\tif ((e as KeyboardEvent).key === 'Enter') searchBookings();\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#if loadingSearch}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{:else if bookings.length === 0}\n\t\t\t\t\t\t\t\t
No bookings found.
\n\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t{#each bookings as b}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t{(() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst date = new Date(b.start_time);\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst now = new Date();\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst today = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst bookingDate = new Date(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdate.getFullYear(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdate.getMonth(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdate.getDate()\n\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst daysDiff = Math.floor(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(bookingDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)\n\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst days = [\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Sunday',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Monday',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Tuesday',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Wednesday',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Thursday',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Friday',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Saturday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst months = [\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Jan',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Feb',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Mar',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Apr',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'May',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'June',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'July',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Aug',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Sept',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Oct',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Nov',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'Dec'\n\t\t\t\t\t\t\t\t\t\t\t\t\t];\n\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst day = days[date.getDay()];\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst dateNum = date.getDate();\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst month = months[date.getMonth()];\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst year = date.getFullYear();\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst currentYear = now.getFullYear();\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst hours = date.getHours();\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst minutes = date.getMinutes().toString().padStart(2, '0');\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst ampm = hours >= 12 ? 'pm' : 'am';\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst hour12 = hours % 12 || 12;\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst time = `${hour12}:${minutes}${ampm}`;\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t// Today\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (daysDiff === 0) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn `Today, ${time}`;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t// Tomorrow\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (daysDiff === 1) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn `Tomorrow, ${time}`;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t// Within next 6 days (2-6 days ahead)\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (daysDiff > 1 && daysDiff <= 6) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn `${day}, ${time}`;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t// Last 6 days (1-6 days ago)\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (daysDiff < 0 && daysDiff >= -6) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn `Last ${day}, ${time}`;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t// Otherwise, full date\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst suffix =\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdateNum === 1 || dateNum === 21 || dateNum === 31\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'st'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: dateNum === 2 || dateNum === 22\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'nd'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: dateNum === 3 || dateNum === 23\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'rd'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'th';\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst yearStr = year !== currentYear ? ` ${year}` : '';\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn `${day} the ${dateNum}${suffix} of ${month}${yearStr}, ${time}`;\n\t\t\t\t\t\t\t\t\t\t\t\t})()}\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t{b.status}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\u2022 {b.user?.full_name || 'Unknown User'}\n\t\t\t\t\t\t\t\t\t\t\t\t\u2022 {(b.services || [])\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.map((s) => s.service_name || 'Unknown Service')\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.join(', ')}\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\tHoliday Hours\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tManage temporary schedules for holidays, closures, and special events.\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t\t{#if exceptionGroupsLoading}\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each Array(2) as _, i}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t{:else}\n\t\t\t\t\t
\n\t\t\t\t\t\t{#if exceptionGroups.length === 0}\n\t\t\t\t\t\t\t

No exception groups found.

\n\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t{#each exceptionGroups as g}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t

{g.name}

\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{g.weekStarts?.length || 0} weeks\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t\t\t\t

{g.description}

\n\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
Applies to weeks:
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t{g.weekStarts\n\t\t\t\t\t\t\t\t\t\t\t\t\t?.slice(0, 3)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.map((w) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Date(w).toLocaleDateString('en-GB', {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tday: 'numeric',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmonth: 'short'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.join(', ')}\n\t\t\t\t\t\t\t\t\t\t\t\t{#if (g.weekStarts?.length ?? 0) > 3}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(+{(g.weekStarts?.length ?? 0) - 3} more)\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t openViewExceptionModal(g)}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex-1\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\tView Details\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\texceptionToDelete = g.id;\n\t\t\t\t\t\t\t\t\t\t\t\tshowDeleteExceptionAlert = true;\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t\n\t\t\t{#if !defaultHoursIsLoading}\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Weekly Schedule

\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\tYour standard operating hours for each day of the week\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{#each defaultHours as row}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
DayStatusOpening TimeClosing TimeTotal Hours
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t{weekdayLabel(row.weekday) === 'Mon'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Monday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: weekdayLabel(row.weekday) === 'Tue'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Tuesday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: weekdayLabel(row.weekday) === 'Wed'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Wednesday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: weekdayLabel(row.weekday) === 'Thu'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Thursday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: weekdayLabel(row.weekday) === 'Fri'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Friday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: weekdayLabel(row.weekday) === 'Sat'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Saturday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Sunday'}\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{row.is_open ? 'Open' : 'Closed'}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{#if row.is_open}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t{row.start_time}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\u2014\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{#if row.is_open}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t{row.end_time}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\u2014\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{#if row.is_open}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t{calculateHours(row.start_time, row.end_time)}\n\t\t\t\t\t\t\t\t\t\t\t\t\thrs\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\u2014\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each defaultHours as row}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{weekdayLabel(row.weekday) === 'Mon'\n\t\t\t\t\t\t\t\t\t\t\t\t? 'Monday'\n\t\t\t\t\t\t\t\t\t\t\t\t: weekdayLabel(row.weekday) === 'Tue'\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Tuesday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: weekdayLabel(row.weekday) === 'Wed'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Wednesday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: weekdayLabel(row.weekday) === 'Thu'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Thursday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: weekdayLabel(row.weekday) === 'Fri'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Friday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: weekdayLabel(row.weekday) === 'Sat'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Saturday'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Sunday'}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{row.is_open ? 'Open' : 'Closed'}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t\t{#if row.is_open}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
Opening
\n\t\t\t\t\t\t\t\t\t\t\t
{row.start_time}
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
Closing
\n\t\t\t\t\t\t\t\t\t\t\t
{row.end_time}
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\tTotal: {calculateHours(row.start_time, row.end_time)} hours\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t
No hours scheduled for this day
\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{:else}\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{#each Array(7) as _, i}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
DayStatusOpening TimeClosing TimeTotal Hours
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each Array(7) as _, i}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/if}\n\t\t
\n\t\t\n\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\tServices Management\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tManage your services - add, edit, toggle availability, or delete services.\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#if servicesLoading}\n\t\t\t\t\t\t\t\t{#each Array(3) as _, i}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t{#each services as service}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\n\t\t\t\t\t
NameDescriptionPriceDurationStatusActions
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
{service.name}\n\t\t\t\t\t\t\t\t\t\t\t{#if service.description}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t{service.description}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\u2014\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t
\u00a3{service.price.toFixed(2)}{service.duration_minutes} min\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{service.is_active ? 'Active' : 'Inactive'}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t toggleService(service.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={servicesUpdating[service.id]}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{servicesUpdating[service.id]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? '...'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: service.is_active\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Deactivate'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Activate'}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t deleteService(service.id)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={servicesUpdating[service.id]}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\tDelete\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t{#if servicesLoading}\n\t\t\t\t\t\t{#each Array(3) as _, i}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t{:else}\n\t\t\t\t\t\t{#each services as service}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t

{service.name}

\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{service.is_active ? 'Active' : 'Inactive'}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t\t\t{#if service.description}\n\t\t\t\t\t\t\t\t\t\t

{service.description}

\n\t\t\t\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\tPrice: \u00a3{service.price.toFixed(2)}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\tDuration:\n\t\t\t\t\t\t\t\t\t\t\t{service.duration_minutes} min\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t toggleService(service.id)}\n\t\t\t\t\t\t\t\t\t\t\tdisabled={servicesUpdating[service.id]}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex-1\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{servicesUpdating[service.id]\n\t\t\t\t\t\t\t\t\t\t\t\t? '...'\n\t\t\t\t\t\t\t\t\t\t\t\t: service.is_active\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Deactivate'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Activate'}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t deleteService(service.id)}\n\t\t\t\t\t\t\t\t\t\t\tdisabled={servicesUpdating[service.id]}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex-1\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tDelete\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\n\t\t\t\t{#if !servicesLoading && services.length === 0}\n\t\t\t\t\t
\n\t\t\t\t\t\tNo services found. Click \"Add Service\" to create your first service.\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t
\n\t\t
\n\t\n\n\t\n\t\n\t\t\n\t\t\t\n\t\t\t\tEdit Default Working Hours\n\t\t\t\t\n\t\t\t\t\tSet the standard open and close times for your business.\n\t\t\t\t\n\t\t\t\n\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each defaultHoursDraft as row}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t
DayOpenStartEnd
{weekdayLabel(row.weekday)}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t\t {\n\t\t\t\t\t\tshowDefaultHoursModal = false;\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\tCancel\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t
\n\t
\n\n\t\n\t\n\t\t\n\t\t\t\n\t\t\t\tSave default hours?\n\t\t\t\t\n\t\t\t\t\tAre you sure you want to save these default hours? This will affect future bookings.\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\tCancel\n\t\t\t\tContinue\n\t\t\t\n\t\t\n\t\n\n\t\n\t\n\t\t\n\t\t\t\n\t\t\t\tCreate Exception Schedule\n\t\t\t\t\n\t\t\t\t\tDefine custom working hours for holidays, closures, or special events.\n\t\t\t\t\n\t\t\t\n\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

Apply to Weeks *

\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\tSelect a date range to add all Mondays within that range\n\t\t\t\t\t\t

\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t{#if exceptionDraft.weekStarts.length > 0}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\tSelected weeks ({exceptionDraft.weekStarts.length}):\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{#each exceptionDraft.weekStarts as week, index}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\tWeek starting: {week}\n\t\t\t\t\t\t\t\t\t\t removeWeek(index)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tRemove\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\n\t\t\t\t\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t

Working Hours for these Weeks *

\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{#each exceptionDraft.hours as row}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
DayOpenStartEnd
{weekdayLabel(row.weekday)}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t\t {\n\t\t\t\t\t\tshowExceptionModal = false;\n\t\t\t\t\t\tresetExceptionForm();\n\t\t\t\t\t}}\n\t\t\t\t\tdisabled={savingHours}\n\t\t\t\t>\n\t\t\t\t\tCancel\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t
\n\t
\n\n\t\n\t\n\t\t\n\t\t\t\n\t\t\t\tDelete exception group?\n\t\t\t\t\n\t\t\t\t\tThis action cannot be undone. This will permanently delete this exception group and all\n\t\t\t\t\tits associated schedule rows.\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t {\n\t\t\t\t\t\texceptionToDelete = undefined;\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\tCancel\n\t\t\t\t\n\t\t\t\tDelete\n\t\t\t\n\t\t\n\t\n\n\t\n\t{#if viewingException}\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t{viewingException.name}\n\t\t\t\t\t\n\t\t\t\t\t\t{viewingException.description || 'Holiday schedule details'}\n\t\t\t\t\t\n\t\t\t\t\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t

Applied to Weeks

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#if viewingException.weekStarts && viewingException.weekStarts.length > 0}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{#each viewingException.weekStarts as week}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\tWeek of {new Date(week).toLocaleDateString('en-GB', {\n\t\t\t\t\t\t\t\t\t\t\t\tday: 'numeric',\n\t\t\t\t\t\t\t\t\t\t\t\tmonth: 'short',\n\t\t\t\t\t\t\t\t\t\t\t\tyear: 'numeric'\n\t\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t

No weeks specified

\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t

Working Hours

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{#each viewingException.hours as row}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
DayStatusStartEnd
{weekdayLabel(row.weekday)}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t{row.is_open ? 'Open' : 'Closed'}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{row.is_open ? row.start_time : '\u2014'}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{row.is_open ? row.end_time : '\u2014'}\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t{/if}\n\n\t\n\t{#if selectedUser}\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tUser: {selectedUser.fn || `${selectedUser.n_first_name} ${selectedUser.n_last_name}`}\n\t\t\t\t\t\n\t\t\t\t\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
Email
\n\t\t\t\t\t\t
{selectedUser.email}
\n\t\t\t\t\t\t
Phone
\n\t\t\t\t\t\t
{selectedUser.phone}
\n\t\t\t\t\t\t
Joined
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{new Date(selectedUser.created_at || '').toLocaleString()}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t
\n\t\t\t\t\t\t
Profile
\n\t\t\t\t\t\t{#if selectedUser.profile_pic_url}\n\t\t\t\t\t\t\t\"profile\"\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
Loyalty stamps
\n\t\t\t\t\t\t\t
{selectedUser.loyalty_stamps ?? 0}
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\n\t\t\t\t
\n\t\t\t\t\t
Recent bookings
\n\t\t\t\t\t{#if bookingUserHistory.length === 0}\n\t\t\t\t\t\t
No recent bookings
\n\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each bookingUserHistory as hb}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
{new Date(hb.start_time).toLocaleString()}
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{hb.status} \u2022 {hb.services.map((s) => s.service_name).join(', ')}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t{/if}\n\n\t{#if selectedBooking}\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tBooking Details\n\t\t\t\t\t
ID: {selectedBooking.id}
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{selectedBooking.status.charAt(0).toUpperCase() + selectedBooking.status.slice(1)}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\tCustomer Information\n\t\t\t\t\t\t

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
Name
\n\t\t\t\t\t\t\t\t
{selectedBooking.user?.full_name || '\u2014'}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
Email
\n\t\t\t\t\t\t\t\t
{selectedBooking.user?.email || '\u2014'}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
Phone
\n\t\t\t\t\t\t\t\t
{selectedBooking.user?.phone || '\u2014'}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
Customer ID
\n\t\t\t\t\t\t\t\t
{selectedBooking.user?.id || '\u2014'}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#if selectedBooking.user?.loyalty_stamps !== undefined && selectedBooking.user?.loyalty_stamps !== null}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
Loyalty Stamps
\n\t\t\t\t\t\t\t\t\t
{selectedBooking.user.loyalty_stamps}
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{#if selectedBooking.user?.referral_code}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
Referral Code
\n\t\t\t\t\t\t\t\t\t
{selectedBooking.user.referral_code}
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{#if selectedBooking.user?.referral_code_uses !== undefined && selectedBooking.user?.referral_code_uses !== null}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
Referral Uses
\n\t\t\t\t\t\t\t\t\t
{selectedBooking.user.referral_code_uses}
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t{#if selectedBooking.user?.notes}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
Customer Notes
\n\t\t\t\t\t\t\t\t
{selectedBooking.user.notes}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\tAppointment Details\n\t\t\t\t\t\t

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
Scheduled Date & Time
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{new Date(selectedBooking.start_time).toLocaleString()}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
Duration
\n\t\t\t\t\t\t\t\t
{selectedBooking.duration_minutes} minutes
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
Created
\n\t\t\t\t\t\t\t\t
{new Date(selectedBooking.created_at).toLocaleString()}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
Last Updated
\n\t\t\t\t\t\t\t\t
{new Date(selectedBooking.updated_at).toLocaleString()}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#if selectedBooking.created_by}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
Created By
\n\t\t\t\t\t\t\t\t\t
{selectedBooking.created_by}
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t{#if selectedBooking.notes}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
Booking Notes
\n\t\t\t\t\t\t\t\t
{selectedBooking.notes}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t{#if selectedBooking.services && selectedBooking.services.length > 0}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\tServices\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{#each selectedBooking.services as service}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
{service.service_name || '\u2014'}
\n\t\t\t\t\t\t\t\t\t\t{#if service.service_description}\n\t\t\t\t\t\t\t\t\t\t\t
{service.service_description}
\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{service.duration_minutes} min\n\t\t\t\t\t\t\t\t\t\t\t\u00a3{service.price?.toFixed(2) || '0.00'}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\tFinancial Summary\n\t\t\t\t\t\t

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\tTotal Amount\n\t\t\t\t\t\t\t\t\u00a3{selectedBooking.total_amount.toFixed(2)}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\tAmount Paid\n\t\t\t\t\t\t\t\t\u00a3{selectedBooking.amount_paid.toFixed(2)}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\tAmount Due\n\t\t\t\t\t\t\t\t 0\n\t\t\t\t\t\t\t\t\t\t? 'text-red-600'\n\t\t\t\t\t\t\t\t\t\t: 'text-green-600'}\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\u00a3{selectedBooking.amount_due.toFixed(2)}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t{#if selectedBooking.payments && selectedBooking.payments.length > 0}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\tPayment History\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{#each selectedBooking.payments as payment}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t{payment.payment_method.replace('_', ' ')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{payment.status}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t{payment.payment_type.charAt(0).toUpperCase() +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpayment.payment_type.slice(1)}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t{#if payment.vendor_code || payment.invoice_number}\n\t\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if payment.vendor_code}Vendor: {payment.vendor_code}{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if payment.vendor_code && payment.invoice_number}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\u2022\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{#if payment.invoice_number}Invoice: #{payment.invoice_number}{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t{#if payment.is_vat_applicable}\n\t\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t
Net: \u00a3{payment.net_amount?.toFixed(2) || '0.00'}
\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tVAT ({(payment.vat_rate || 0) * 100}%): \u00a3{payment.vat_amount?.toFixed(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t2\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t) || '0.00'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t{new Date(payment.created_at).toLocaleString()}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\u00a3{payment.amount.toFixed(2)}\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t{/if}\n\n\t\n\t{#if showServiceModal}\n\t\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tAdd New Service\n\t\t\t\t\tCreate a new service that customers can book.\n\t\t\t\t\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t{#if serviceErrors.name}\n\t\t\t\t\t\t\t

{serviceErrors.name}

\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\u00a3\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#if serviceErrors.price}\n\t\t\t\t\t\t\t\t

{serviceErrors.price}

\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#if serviceErrors.duration_minutes}\n\t\t\t\t\t\t\t\t

{serviceErrors.duration_minutes}

\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#if serviceErrors.minimum_age_required}\n\t\t\t\t\t\t\t\t

{serviceErrors.minimum_age_required}

\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t

0 for no age restriction

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t\t {\n\t\t\t\t\t\t\tshowServiceModal = false;\n\t\t\t\t\t\t\tresetServiceForm();\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tdisabled={creatingService}\n\t\t\t\t\t>\n\t\t\t\t\t\tCancel\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t{/if}\n{/if}", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\nimport { error } from '@sveltejs/kit';\nimport type { RequestHandler } from './$types';\n", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\nconst BACKEND_URL =\n import.meta.env.VITE_BACKEND_URL || 'http://localhost:8080';\n", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\nasync function proxyRequest(request: Request, path: string) {", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\n const incomingUrl = new URL(request.url);\n", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\n const queryString = incomingUrl.search;\n", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\n const url = `${BACKEND_URL}/api/${path}${queryString}`;\n", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\n try {", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\n const headers = new Headers(request.headers);\n headers.delete('host');\n", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\n const backendRes = await fetch(url, {\n method: request.method,\n headers,\n body: ['GET', 'HEAD'].includes(request.method)\n ? undefined\n : await request.text()\n });\n", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\n // Forward everything transparently", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\n const resHeaders = new Headers(backendRes.headers);", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\n return new Response(backendRes.body, {\n status: backendRes.status,\n headers: resHeaders\n });", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\n } catch (err) {\n console.error('Proxy error:', err);\n return error(502, 'Backend unreachable');\n }\n}\n\n", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\nexport const GET: RequestHandler = ({ request, params }) => proxyRequest(request, params.path);", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\nexport const POST: RequestHandler = ({ request, params }) => proxyRequest(request, params.path);", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\nexport const PUT: RequestHandler = ({ request, params }) => proxyRequest(request, params.path);", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\nexport const DELETE: RequestHandler = ({ request, params }) => proxyRequest(request, params.path);", + "File: /home/popertots/Crussell/frontend/src/routes/api/[...path]/+server.ts\nLanguage: typescript\n\nexport const PATCH: RequestHandler = ({ request, params }) => proxyRequest(request, params.path);", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n// src/lib/stores/auth.svelte.ts\nimport { browser } from '$app/environment';\nimport { goto } from '$app/navigation';\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\nexport type UserRole = 'unverified_email' | 'verified_email' | 'admin' | 'guest' | 'affiliate';\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\nexport interface DecodedToken {\n user_id: string;\n role: UserRole;\n exp: number;\n}\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\nexport interface User {\n id: string;\n email: string;\n role: UserRole;\n firstName: string;\n lastName: string;\n phone?: string;\n dateOfBirth?: string;\n loyaltyStamps?: number;\n referralCode?: string;\n profilePicUrl?: string;\n}\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\nclass AuthStore {\n private token = $state(null);\n private user = $state(null);\n private loading = $state(true);\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n constructor() {", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n if (browser) {\n this.initializeAuth();\n }\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n get isAuthenticated() {\n return this.token !== null && this.user !== null;\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n get currentUser() {\n return this.user;\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n get currentToken() {\n return this.token;\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n get isLoading() {\n return this.loading;\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n get hasLoaded() {\n return !this.loading;\n }\n\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n private initializeAuth() {", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const storedToken = localStorage.getItem('authToken');", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n if (storedToken) {", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const decoded = this.decodeToken(storedToken);", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n if (decoded && !this.isTokenExpired(decoded)) {\n this.token = storedToken;", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n // Set basic user info from token", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n this.user = {\n id: decoded.user_id,\n role: decoded.role,\n email: '',\n firstName: '',\n lastName: ''\n };\n this.fetchUserProfile();", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n } else {\n this.clearAuth();\n }\n }\n this.loading = false;\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n private decodeToken(token: string): DecodedToken | null {", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n try {", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const payload = token.split('.')[1];", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const decoded = JSON.parse(atob(payload));\n return decoded;", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n } catch (e) {\n console.error('Failed to decode token:', e);\n return null;\n }\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n private isTokenExpired(decoded: DecodedToken): boolean {\n return decoded.exp * 1000 < Date.now();\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n // Simple setters - UI handles the API calls", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n setToken(token: string) {\n this.token = token;", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n if (browser) {\n localStorage.setItem('authToken', token);\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n // Decode to get basic info", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const decoded = this.decodeToken(token);", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n if (decoded) {", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n this.user = {\n id: decoded.user_id,\n role: decoded.role,\n email: '',\n firstName: '',\n lastName: ''\n };\n this.fetchUserProfile();\n }\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n private async fetchUserProfile() {\n if (!this.token) return;\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n try {", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const response = await fetch('/api/user/profile', {", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n headers: {\n 'Authorization': `Bearer ${this.token}`\n }\n });\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n if (!response.ok) {\n throw new Error('Failed to fetch profile');\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const userData = await response.json();\n this.user = userData;", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n } catch (error) {\n console.error('Failed to fetch user profile:', error);\n this.clearAuth();\n }\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n // inside AuthStore", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n logout = () => {\n this.clearAuth();\n goto('/');\n };\n\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n private clearAuth() {\n this.token = null;\n this.user = null;", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n if (browser) {\n localStorage.removeItem('authToken');\n }\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n hasRole(requiredRole: UserRole | UserRole[]): boolean {\n if (!this.user) return false;\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const roles = Array.isArray(requiredRole) ? requiredRole : [requiredRole];\n return roles.includes(this.user.role);\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n isAdmin(): boolean {\n return this.hasRole('admin');\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n isVerified(): boolean {\n return this.hasRole(['verified_email', 'admin']);\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n // Refresh token before it expires", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n async refreshTokenIfNeeded() {\n if (!this.token) return;\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const decoded = this.decodeToken(this.token);", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n if (!decoded) {\n this.clearAuth();\n return;\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n // Refresh if token expires in less than 2 weeks", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const threeDays = 2 * 7 * 24 * 60 * 60 * 1000;", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n if (decoded.exp * 1000 - Date.now() < threeDays) {", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n try {", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const response = await fetch('/api/refresh-token', {\n method: 'POST',", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n headers: {\n 'Authorization': `Bearer ${this.token}`\n }\n });\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n if (response.ok) {", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n const data = await response.json();\n this.setToken(data.token);", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n } else {\n this.clearAuth();\n }", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n } catch (error) {\n console.error('Token refresh failed:', error);\n }\n }\n }\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n // Manual refresh method", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\n async refreshProfile() {\n await this.fetchUserProfile();\n }\n}\n", + "File: /home/popertots/Crussell/frontend/src/lib/stores/auth.svelte.ts\nLanguage: typescript\n\nexport const authStore = new AuthStore();", + "File: /home/popertots/Crussell/frontend/src/lib/components/layout/PortfolioCarousel.svelte\nType: style\n\n\t@keyframes scroll {\n\t\t0% {\n\t\t\ttransform: translateX(0);\n\t\t}\n\t\t100% {\n\t\t\ttransform: translateX(-50%);\n\t\t}\n\t}\n\n\t.animate-scroll {\n\t\tanimation: scroll 40s linear infinite;\n\t\twidth: calc(256px * 16); /* 16 images total (8 duplicated) * 256px width */\n\t}\n\n\t/* images scale on hover */\n\t.group:hover img {\n\t\ttransform: scale(1.05);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/layout/PortfolioCarousel.svelte\nType: markup\n
\n\t
\n\t\t

Our Portfolio

\n\t
\n\n\t
\n\t\t
\n\t\t\t{#each [...portfolioImages, ...portfolioImages] as image}\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t{/each}\n\t\t
\n\t
\n
", + "File: /home/popertots/Crussell/frontend/src/lib/components/layout/ContactCard.svelte\nType: markup\n
\n\t
\n\t\t\n\t\t
\n\t\t\t
\n\t\t\t\t{altText}\n\t\t\t
\n\t\t
\n\t\t\n\t\t
\n\t\t\t

{name}

\n\t\t\t

{role}

\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t{address}\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t\n\t\t\t\t{phone}\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t\n\t\t\t\t{email}\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t\n\t\t\t\t@{instagram}\n\t\t\t
\n\t\t
\n\t
\n
", + "File: /home/popertots/Crussell/frontend/src/lib/components/layout/RequiredLabel.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/layout/NavBar.svelte\nType: function\nName: toggleMenu\nDoc: \nCode:\n\tfunction toggleMenu() {\n\t\tmobileMenuOpen = !mobileMenuOpen;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/layout/NavBar.svelte\nType: function\nName: canShow\nDoc: \nCode:\n\tconst canShow = (link: { href: string; label: string; showWhen: string; width: string }) => {\n\t\tif (authStore.isLoading) return true; // keep skeleton placeholders\n\n\t\t// hide booking link for admins\n\t\tif (link.href === '/book' && authStore.currentUser?.role === 'admin') return false;\n\t\tif (link.href === '/contact' && authStore.currentUser?.role === 'admin') return false;\n\n\t\tif (link.showWhen === 'always') return true;\n\t\tif (link.showWhen === 'guest' && !authStore.isAuthenticated) return true;\n\t\tif (link.showWhen === 'auth' && authStore.isAuthenticated) return true;\n\t\tif (link.showWhen === 'admin' && authStore.currentUser?.role === 'admin') return true;\n\t\treturn false;\n\t};\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/layout/NavBar.svelte\nType: style\n\n\t.frosty-nav {\n\t\tbackdrop-filter: saturate(180%) blur(10px);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/layout/NavBar.svelte\nType: markup\n\n\t
\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t{#each links as link}\n\t\t\t\t\t{#if canShow(link)}\n\t\t\t\t\t\t{#if authStore.isLoading}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t{link.label}\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t{/if}\n\t\t\t\t{/each}\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t{#if !authStore.isLoading && !authStore.isAuthenticated && $page.url.pathname !== '/login'}\n\t\t\t\t\t\n\t\t\t\t{/if}\n\t\t\t\t{#if authStore.isLoading}\n\t\t\t\t\t\n\t\t\t\t{/if}\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t
\n\n\t\n\t{#if mobileMenuOpen}\n\t\t
\n\t\t\t
\n\t\t\t\t{#each links as link}\n\t\t\t\t\t{#if canShow(link)}\n\t\t\t\t\t\t{#if authStore.isLoading}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{link.label}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t{/if}\n\t\t\t\t{/each}\n\n\t\t\t\t{#if authStore.isLoading}\n\t\t\t\t\t\n\t\t\t\t{:else if !authStore.isAuthenticated}\n\t\t\t\t\t\n\t\t\t\t{/if}\n\t\t\t
\n\t\t
\n\t{/if}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/layout/Calendar.svelte\nType: markup\n\n\t\n\t\t
\n\t\t\t bookedDates.some((d) => d.compare(date) === 0)}\n\t\t\t\tclass=\"bg-transparent p-0 [--cell-size:--spacing(10)] data-unavailable:line-through data-unavailable:opacity-100 md:[--cell-size:--spacing(12)] [&_[data-outside-month]]:hidden\"\n\t\t\t\tweekdayFormat=\"short\"\n\t\t\t/>\n\t\t
\n\t\t\n\t\t\t
\n\t\t\t\t{#each timeSlots as time (time)}\n\t\t\t\t\t (selectedTime = time)}\n\t\t\t\t\t\tclass=\"w-full\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{time}\n\t\t\t\t\t\n\t\t\t\t{/each}\n\t\t\t
\n\t\t\n\t
\n\t\n\t\t
\n\t\t\t{#if value && selectedTime}\n\t\t\t\tYour meeting is booked for\n\t\t\t\t\n\t\t\t\t\t{value.toDate(getLocalTimeZone()).toLocaleDateString('en-US', {\n\t\t\t\t\t\tweekday: 'long',\n\t\t\t\t\t\tday: 'numeric',\n\t\t\t\t\t\tmonth: 'short'\n\t\t\t\t\t})}\n\t\t\t\t\n\t\t\t\tat {selectedTime}.\n\t\t\t{:else}\n\t\t\t\tSelect a date and time for your meeting.\n\t\t\t{/if}\n\t\t
\n\t\t\n\t\t\tContinue\n\t\t\n\t
\n
", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/file-drop-zone.svelte\nType: function\nName: handleDrop\nDoc: \nCode:\n\tfunction handleDrop(e: DragEvent) {\n\t\te.preventDefault();\n\t\tdragging = false;\n\t\tconst files = Array.from(e.dataTransfer?.files ?? []);\n\t\tif (files.length) {\n\t\t\tonfiles(files);\n\t\t}\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/file-drop-zone.svelte\nType: function\nName: handleDragOver\nDoc: \nCode:\n\tfunction handleDragOver(e: DragEvent) {\n\t\te.preventDefault();\n\t\tdragging = true;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/file-drop-zone.svelte\nType: function\nName: handleDragLeave\nDoc: \nCode:\n\tfunction handleDragLeave(e: DragEvent) {\n\t\te.preventDefault();\n\t\tdragging = false;\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/file-drop-zone.svelte\nType: function\nName: handleFileSelect\nDoc: \nCode:\n\tfunction handleFileSelect(e: Event) {\n\t\tconst input = e.target as HTMLInputElement;\n\t\tconst files = Array.from(input.files ?? []);\n\t\tif (files.length) {\n\t\t\tonfiles(files);\n\t\t}\n\t\t// IMPORTANT: Reset the input value so the same file can be selected again\n\t\tinput.value = '';\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/file-drop-zone.svelte\nType: function\nName: openFileSelect\nDoc: \nCode:\n\tfunction openFileSelect() {\n\t\tfileInputEl.click();\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/file-drop-zone.svelte\nType: markup\n {\n\t\t// Allow keyboard users to trigger the click with Enter or Space\n\t\tif (e.key === 'Enter' || e.key === ' ') {\n\t\t\te.preventDefault();\n\t\t\topenFileSelect();\n\t\t}\n\t}}\n\tclass=\"rounded-lg border-2 border-dashed p-6 text-center transition-colors\n\t{dragging ? 'border-primary bg-primary/5' : 'border-gray-300'}\"\n>\n\t\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/separator/index.ts\nLanguage: typescript\n\nimport Root from \"./separator.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/separator/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/separator/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Separator,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/separator/separator.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-cell.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-months.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-grid.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-month-select.svelte\nType: markup\n\n\t\n\t\t{#snippet child({ props, monthItems, selectedMonthItem })}\n\t\t\t\n\t\t\tsvg]:text-muted-foreground flex h-8 select-none items-center gap-1 rounded-md pl-2 pr-1 text-sm font-medium [&>svg]:size-3.5\"\n\t\t\t\taria-hidden=\"true\"\n\t\t\t>\n\t\t\t\t{monthItems.find((item) => item.value === value)?.label || selectedMonthItem.label}\n\t\t\t\t\n\t\t\t\n\t\t{/snippet}\n\t\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-year-select.svelte\nType: markup\n\n\t\n\t\t{#snippet child({ props, yearItems, selectedYearItem })}\n\t\t\t\n\t\t\tsvg]:text-muted-foreground flex h-8 select-none items-center gap-1 rounded-md pl-2 pr-1 text-sm font-medium [&>svg]:size-3.5\"\n\t\t\t\taria-hidden=\"true\"\n\t\t\t>\n\t\t\t\t{yearItems.find((item) => item.value === value)?.label || selectedYearItem.label}\n\t\t\t\t\n\t\t\t\n\t\t{/snippet}\n\t\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-next-button.svelte\nType: markup\n{#snippet Fallback()}\n\t\n{/snippet}\n\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-header.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-head-cell.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/index.ts\nLanguage: typescript\n\nimport Root from \"./calendar.svelte\";\nimport Cell from \"./calendar-cell.svelte\";\nimport Day from \"./calendar-day.svelte\";\nimport Grid from \"./calendar-grid.svelte\";\nimport Header from \"./calendar-header.svelte\";\nimport Months from \"./calendar-months.svelte\";\nimport GridRow from \"./calendar-grid-row.svelte\";\nimport Heading from \"./calendar-heading.svelte\";\nimport GridBody from \"./calendar-grid-body.svelte\";\nimport GridHead from \"./calendar-grid-head.svelte\";\nimport HeadCell from \"./calendar-head-cell.svelte\";\nimport NextButton from \"./calendar-next-button.svelte\";\nimport PrevButton from \"./calendar-prev-button.svelte\";\nimport MonthSelect from \"./calendar-month-select.svelte\";\nimport YearSelect from \"./calendar-year-select.svelte\";\nimport Month from \"./calendar-month.svelte\";\nimport Nav from \"./calendar-nav.svelte\";\nimport Caption from \"./calendar-caption.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/index.ts\nLanguage: typescript\n\nexport {\n\tDay,\n\tCell,\n\tGrid,\n\tHeader,\n\tMonths,\n\tGridRow,\n\tHeading,\n\tGridBody,\n\tGridHead,\n\tHeadCell,\n\tNextButton,\n\tPrevButton,\n\tNav,\n\tMonth,\n\tYearSelect,\n\tMonthSelect,\n\tCaption,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Calendar,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-grid-head.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-caption.svelte\nType: function\nName: formatYear\nDoc: \nCode:\n\tfunction formatYear(date: DateValue) {\n\t\tconst dateObj = date.toDate(getLocalTimeZone());\n\t\tif (typeof yearFormat === \"function\") return yearFormat(dateObj.getFullYear());\n\t\treturn new DateFormatter(locale, { year: yearFormat }).format(dateObj);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-caption.svelte\nType: function\nName: formatMonth\nDoc: \nCode:\n\tfunction formatMonth(date: DateValue) {\n\t\tconst dateObj = date.toDate(getLocalTimeZone());\n\t\tif (typeof monthFormat === \"function\") return monthFormat(dateObj.getMonth() + 1);\n\t\treturn new DateFormatter(locale, { month: monthFormat }).format(dateObj);\n\t}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-caption.svelte\nType: markup\n{#snippet MonthSelect()}\n\t {\n\t\t\tif (!placeholder) return;\n\t\t\tconst v = Number.parseInt(e.currentTarget.value);\n\t\t\tconst newPlaceholder = placeholder.set({ month: v });\n\t\t\tplaceholder = newPlaceholder.subtract({ months: monthIndex });\n\t\t}}\n\t/>\n{/snippet}\n\n{#snippet YearSelect()}\n\t\n{/snippet}\n\n{#if captionLayout === \"dropdown\"}\n\t{@render MonthSelect()}\n\t{@render YearSelect()}\n{:else if captionLayout === \"dropdown-months\"}\n\t{@render MonthSelect()}\n\t{#if placeholder}\n\t\t{formatYear(placeholder)}\n\t{/if}\n{:else if captionLayout === \"dropdown-years\"}\n\t{#if placeholder}\n\t\t{formatMonth(placeholder)}\n\t{/if}\n\t{@render YearSelect()}\n{:else}\n\t{formatMonth(month)} {formatYear(month)}\n{/if}", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-heading.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-prev-button.svelte\nType: markup\n{#snippet Fallback()}\n\t\n{/snippet}\n\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-nav.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-day.svelte\nType: markup\nspan]:text-xs [&>span]:opacity-70\",\n\t\tclassName\n\t)}\n\t{...restProps}\n/>", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-grid-body.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-grid-row.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar.svelte\nType: markup\n\n\n\t{#snippet children({ months, weekdays })}\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t{#each months as month, monthIndex (month)}\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{#each weekdays as weekday (weekday)}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{weekday.slice(0, 2)}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each month.weeks as weekDates (weekDates)}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{#each weekDates as date (date)}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{#if day}\n\t\t\t\t\t\t\t\t\t\t\t\t{@render day({\n\t\t\t\t\t\t\t\t\t\t\t\t\tday: date,\n\t\t\t\t\t\t\t\t\t\t\t\t\toutsideMonth: !isEqualMonth(date, month.value)\n\t\t\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t{/each}\n\t\t\n\t{/snippet}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/calendar/calendar-month.svelte\nType: markup\n
\n\t{@render children?.()}\n
", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/card/card.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/card/card-description.svelte\nType: markup\n\n\t{@render children?.()}\n

", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/card/card-footer.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/card/index.ts\nLanguage: typescript\n\nimport Root from \"./card.svelte\";\nimport Content from \"./card-content.svelte\";\nimport Description from \"./card-description.svelte\";\nimport Footer from \"./card-footer.svelte\";\nimport Header from \"./card-header.svelte\";\nimport Title from \"./card-title.svelte\";\nimport Action from \"./card-action.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/card/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,\n\tContent,\n\tDescription,\n\tFooter,\n\tHeader,\n\tTitle,\n\tAction,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/card/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Card,\n\tContent as CardContent,\n\tDescription as CardDescription,\n\tFooter as CardFooter,\n\tHeader as CardHeader,\n\tTitle as CardTitle,\n\tAction as CardAction,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/card/card-action.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/card/card-content.svelte\nType: markup\n
\n\t{@render children?.()}\n
", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/card/card-header.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/card/card-title.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/checkbox/index.ts\nLanguage: typescript\n\nimport Root from \"./checkbox.svelte\";", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/checkbox/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/checkbox/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Checkbox,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/checkbox/checkbox.svelte\nType: markup\n\n\t{#snippet children({ checked, indeterminate })}\n\t\t
\n\t\t\t{#if checked}\n\t\t\t\t\n\t\t\t{:else if indeterminate}\n\t\t\t\t\n\t\t\t{/if}\n\t\t
\n\t{/snippet}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/table/table-header.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/table/table-row.svelte\nType: markup\nsvelte-css-wrapper]:[&>th,td]:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors\",\n\t\tclassName\n\t)}\n\t{...restProps}\n>\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/table/table-footer.svelte\nType: markup\ntr]:last:border-b-0\", className)}\n\t{...restProps}\n>\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/table/index.ts\nLanguage: typescript\n\nimport Root from \"./table.svelte\";\nimport Body from \"./table-body.svelte\";\nimport Caption from \"./table-caption.svelte\";\nimport Cell from \"./table-cell.svelte\";\nimport Footer from \"./table-footer.svelte\";\nimport Head from \"./table-head.svelte\";\nimport Header from \"./table-header.svelte\";\nimport Row from \"./table-row.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/table/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,\n\tBody,\n\tCaption,\n\tCell,\n\tFooter,\n\tHead,\n\tHeader,\n\tRow,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/table/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Table,\n\tBody as TableBody,\n\tCaption as TableCaption,\n\tCell as TableCell,\n\tFooter as TableFooter,\n\tHead as TableHead,\n\tHeader as TableHeader,\n\tRow as TableRow,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/table/table-cell.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/table/table-head.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/table/table-caption.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/table/table.svelte\nType: markup\n
\n\t\n\t\t{@render children?.()}\n\t\n
", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/table/table-body.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/textarea/index.ts\nLanguage: typescript\n\nimport Root from \"./textarea.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/textarea/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/textarea/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Textarea,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/textarea/textarea.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/index.ts\nLanguage: typescript\n\nimport { AlertDialog as AlertDialogPrimitive } from \"bits-ui\";\nimport Trigger from \"./alert-dialog-trigger.svelte\";\nimport Title from \"./alert-dialog-title.svelte\";\nimport Action from \"./alert-dialog-action.svelte\";\nimport Cancel from \"./alert-dialog-cancel.svelte\";\nimport Footer from \"./alert-dialog-footer.svelte\";\nimport Header from \"./alert-dialog-header.svelte\";\nimport Overlay from \"./alert-dialog-overlay.svelte\";\nimport Content from \"./alert-dialog-content.svelte\";\nimport Description from \"./alert-dialog-description.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/index.ts\nLanguage: typescript\n\nconst Root = AlertDialogPrimitive.Root;", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/index.ts\nLanguage: typescript\n\nconst Portal = AlertDialogPrimitive.Portal;\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,\n\tTitle,\n\tAction,\n\tCancel,\n\tPortal,\n\tFooter,\n\tHeader,\n\tTrigger,\n\tOverlay,\n\tContent,\n\tDescription,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as AlertDialog,\n\tTitle as AlertDialogTitle,\n\tAction as AlertDialogAction,\n\tCancel as AlertDialogCancel,\n\tPortal as AlertDialogPortal,\n\tFooter as AlertDialogFooter,\n\tHeader as AlertDialogHeader,\n\tTrigger as AlertDialogTrigger,\n\tOverlay as AlertDialogOverlay,\n\tContent as AlertDialogContent,\n\tDescription as AlertDialogDescription,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte\nType: markup\n\n\t\n\t\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/dialog-title.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/dialog-overlay.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/dialog-description.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/dialog-trigger.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/dialog-header.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/index.ts\nLanguage: typescript\n\nimport { Dialog as DialogPrimitive } from \"bits-ui\";\n\nimport Title from \"./dialog-title.svelte\";\nimport Footer from \"./dialog-footer.svelte\";\nimport Header from \"./dialog-header.svelte\";\nimport Overlay from \"./dialog-overlay.svelte\";\nimport Content from \"./dialog-content.svelte\";\nimport Description from \"./dialog-description.svelte\";\nimport Trigger from \"./dialog-trigger.svelte\";\nimport Close from \"./dialog-close.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/index.ts\nLanguage: typescript\n\nconst Root = DialogPrimitive.Root;", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/index.ts\nLanguage: typescript\n\nconst Portal = DialogPrimitive.Portal;\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,\n\tTitle,\n\tPortal,\n\tFooter,\n\tHeader,\n\tTrigger,\n\tOverlay,\n\tContent,\n\tDescription,\n\tClose,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Dialog,\n\tTitle as DialogTitle,\n\tPortal as DialogPortal,\n\tFooter as DialogFooter,\n\tHeader as DialogHeader,\n\tTrigger as DialogTrigger,\n\tOverlay as DialogOverlay,\n\tContent as DialogContent,\n\tDescription as DialogDescription,\n\tClose as DialogClose,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/dialog-footer.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/dialog-content.svelte\nType: markup\n\n\t\n\t\n\t\t{@render children?.()}\n\t\t{#if showCloseButton}\n\t\t\t\n\t\t\t\t\n\t\t\t\tClose\n\t\t\t\n\t\t{/if}\n\t\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/dialog/dialog-close.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/select-group.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/select-trigger.svelte\nType: markup\n\n\t{@render children?.()}\n\t\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/select-separator.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/select-label.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/index.ts\nLanguage: typescript\n\nimport { Select as SelectPrimitive } from \"bits-ui\";\n\nimport Group from \"./select-group.svelte\";\nimport Label from \"./select-label.svelte\";\nimport Item from \"./select-item.svelte\";\nimport Content from \"./select-content.svelte\";\nimport Trigger from \"./select-trigger.svelte\";\nimport Separator from \"./select-separator.svelte\";\nimport ScrollDownButton from \"./select-scroll-down-button.svelte\";\nimport ScrollUpButton from \"./select-scroll-up-button.svelte\";\nimport GroupHeading from \"./select-group-heading.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/index.ts\nLanguage: typescript\n\nconst Root = SelectPrimitive.Root;\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,\n\tGroup,\n\tLabel,\n\tItem,\n\tContent,\n\tTrigger,\n\tSeparator,\n\tScrollDownButton,\n\tScrollUpButton,\n\tGroupHeading,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Select,\n\tGroup as SelectGroup,\n\tLabel as SelectLabel,\n\tItem as SelectItem,\n\tContent as SelectContent,\n\tTrigger as SelectTrigger,\n\tSeparator as SelectSeparator,\n\tScrollDownButton as SelectScrollDownButton,\n\tScrollUpButton as SelectScrollUpButton,\n\tGroupHeading as SelectGroupHeading,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/select-item.svelte\nType: markup\n\n\t{#snippet children({ selected, highlighted })}\n\t\t\n\t\t\t{#if selected}\n\t\t\t\t\n\t\t\t{/if}\n\t\t\n\t\t{#if childrenProp}\n\t\t\t{@render childrenProp({ selected, highlighted })}\n\t\t{:else}\n\t\t\t{label || value}\n\t\t{/if}\n\t{/snippet}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/select-content.svelte\nType: markup\n\n\t\n\t\t\n\t\t\n\t\t\t{@render children?.()}\n\t\t\n\t\t\n\t\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/select-scroll-down-button.svelte\nType: markup\n\n\t\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/select-scroll-up-button.svelte\nType: markup\n\n\t\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/select/select-group-heading.svelte\nType: markup\n\n\t{@render children?.()}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/form-legend.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/form-label.svelte\nType: markup\n\n\t{#snippet child({ props })}\n\t\t\n\t\t\t{@render children?.()}\n\t\t\n\t{/snippet}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/form-element-field.svelte\nType: script\n, U extends FormPathLeaves\">\n\timport * as FormPrimitive from \"formsnap\";\n\timport type { FormPathLeaves } from \"sveltekit-superforms\";\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\timport { cn, type WithElementRef, type WithoutChildren } from \"$lib/utils.js\";\n\n\tlet {\n\t\tref = $bindable(null),\n\t\tclass: className,\n\t\tform,\n\t\tname,\n\t\tchildren: childrenProp,\n\t\t...restProps\n\t}: WithoutChildren>> &\n\t\tFormPrimitive.ElementFieldProps = $props();\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/form-element-field.svelte\nType: markup\n\n\t{#snippet children({ constraints, errors, tainted, value })}\n\t\t
\n\t\t\t{@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })}\n\t\t
\n\t{/snippet}\n
", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/form-description.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/form-fieldset.svelte\nType: script\n, U extends FormPath\">\n\timport * as FormPrimitive from \"formsnap\";\n\timport type { FormPath } from \"sveltekit-superforms\";\n\timport { cn, type WithoutChild } from \"$lib/utils.js\";\n\n\tlet {\n\t\tref = $bindable(null),\n\t\tclass: className,\n\t\tform,\n\t\tname,\n\t\t...restProps\n\t}: WithoutChild> = $props();\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/form-fieldset.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/index.ts\nLanguage: typescript\n\nimport * as FormPrimitive from \"formsnap\";\nimport Description from \"./form-description.svelte\";\nimport Label from \"./form-label.svelte\";\nimport FieldErrors from \"./form-field-errors.svelte\";\nimport Field from \"./form-field.svelte\";\nimport Fieldset from \"./form-fieldset.svelte\";\nimport Legend from \"./form-legend.svelte\";\nimport ElementField from \"./form-element-field.svelte\";\nimport Button from \"./form-button.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/index.ts\nLanguage: typescript\n\nconst Control = FormPrimitive.Control;\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/index.ts\nLanguage: typescript\n\nexport {\n\tField,\n\tControl,\n\tLabel,\n\tButton,\n\tFieldErrors,\n\tDescription,\n\tFieldset,\n\tLegend,\n\tElementField,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/index.ts\nLanguage: typescript\n\n\t//\n\tField as FormField,\n\tControl as FormControl,\n\tDescription as FormDescription,\n\tLabel as FormLabel,\n\tFieldErrors as FormFieldErrors,\n\tFieldset as FormFieldset,\n\tLegend as FormLegend,\n\tElementField as FormElementField,\n\tButton as FormButton,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/form-field-errors.svelte\nType: markup\n\n\t{#snippet children({ errors, errorProps })}\n\t\t{#if childrenProp}\n\t\t\t{@render childrenProp({ errors, errorProps })}\n\t\t{:else}\n\t\t\t{#each errors as error (error)}\n\t\t\t\t
{error}
\n\t\t\t{/each}\n\t\t{/if}\n\t{/snippet}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/form-field.svelte\nType: script\n, U extends FormPath\">\n\timport * as FormPrimitive from \"formsnap\";\n\timport type { FormPath } from \"sveltekit-superforms\";\n\timport { cn, type WithElementRef, type WithoutChildren } from \"$lib/utils.js\";\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\n\tlet {\n\t\tref = $bindable(null),\n\t\tclass: className,\n\t\tform,\n\t\tname,\n\t\tchildren: childrenProp,\n\t\t...restProps\n\t}: FormPrimitive.FieldProps &\n\t\tWithoutChildren>> = $props();\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/form-field.svelte\nType: markup\n\n\t{#snippet children({ constraints, errors, tainted, value })}\n\t\t\n\t\t\t{@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })}\n\t\t\n\t{/snippet}\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/form/form-button.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/input/index.ts\nLanguage: typescript\n\nimport Root from \"./input.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/input/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/input/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Input,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/input/input.svelte\nType: type\nName: InputType\nDoc: \nCode:\n\ttype InputType = Exclude;\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/input/input.svelte\nType: type\nName: Props\nDoc: \nCode:\n\ttype Props = WithElementRef<\n\t\tOmit &\n\t\t\t({ type: \"file\"; files?: FileList } | { type?: InputType; files?: undefined })\n\t>;\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/input/input.svelte\nType: markup\n{#if type === \"file\"}\n\t\n{:else}\n\t\n{/if}", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/label/index.ts\nLanguage: typescript\n\nimport Root from \"./label.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/label/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/label/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Label,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/label/label.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/sonner/index.ts\nLanguage: typescript\n\nexport { default as Toaster } from \"./sonner.svelte\";", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/sonner/sonner.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/skeleton/index.ts\nLanguage: typescript\n\nimport Root from \"./skeleton.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/skeleton/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/skeleton/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Skeleton,\n};", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/skeleton/skeleton.svelte\nType: markup\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/button/button.svelte\nType: type\nName: ButtonVariant\nDoc: \nCode:\n\texport type ButtonVariant = VariantProps[\"variant\"];\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/button/button.svelte\nType: type\nName: ButtonSize\nDoc: \nCode:\n\texport type ButtonSize = VariantProps[\"size\"];\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/button/button.svelte\nType: type\nName: ButtonProps\nDoc: \nCode:\n\texport type ButtonProps = WithElementRef &\n\t\tWithElementRef & {\n\t\t\tvariant?: ButtonVariant;\n\t\t\tsize?: ButtonSize;\n\t\t};\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/button/button.svelte\nType: markup\n{#if href}\n\t\n\t\t{@render children?.()}\n\t\n{:else}\n\t\n\t\t{@render children?.()}\n\t\n{/if}", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/button/index.ts\nLanguage: typescript\n\nimport Root, {\n\ttype ButtonProps,\n\ttype ButtonSize,\n\ttype ButtonVariant,\n\tbuttonVariants,\n} from \"./button.svelte\";\n", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/button/index.ts\nLanguage: typescript\n\nexport {\n\tRoot,\n\ttype ButtonProps as Props,", + "File: /home/popertots/Crussell/frontend/src/lib/components/ui/button/index.ts\nLanguage: typescript\n\n\t//\n\tRoot as Button,\n\tbuttonVariants,\n\ttype ButtonProps,\n\ttype ButtonSize,\n\ttype ButtonVariant,\n};", + "File: /home/popertots/Crussell/backend/mw/auth.go\nType: type\nName: contextKey\nCode:\ntype contextKey string\n", + "File: /home/popertots/Crussell/backend/mw/auth.go\nType: var\nName: UserIDKey\nCode:\n\tUserIDKey contextKey = \"user_id\"\n", + "File: /home/popertots/Crussell/backend/mw/auth.go\nType: var\nName: UserRoleKey\nCode:\n\tUserRoleKey contextKey = \"user_role\"\n", + "File: /home/popertots/Crussell/backend/mw/auth.go\nType: func\nName: RequireAuth\nDoc:\nRequireAuth middleware - validates JWT and adds user info to context\n\nCode:\nfunc RequireAuth(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tauthHeader := r.Header.Get(\"Authorization\")\n\t\tif authHeader == \"\" || !strings.HasPrefix(authHeader, \"Bearer \") {\n\t\t\thttp.Error(w, \"missing or invalid authorization header\", http.StatusUnauthorized)\n\t\t\treturn\n\t\t}\n\n\t\ttokenString := strings.TrimPrefix(authHeader, \"Bearer \")\n\n\t\tuserID, role, err := auth.VerifyToken(tokenString, r.Context())\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"invalid token\", http.StatusUnauthorized)\n\t\t\treturn\n\t\t}\n\n\t\t// Add user info to context\n\t\tctx := context.WithValue(r.Context(), UserIDKey, userID)\n\t\tctx = context.WithValue(ctx, UserRoleKey, role)\n\n\t\tnext.ServeHTTP(w, r.WithContext(ctx))\n\t})\n}\n", + "File: /home/popertots/Crussell/backend/mw/auth.go\nType: func\nName: RequireRole\nDoc:\nRequireRole middleware - checks if user has required role(s)\n\nCode:\nfunc RequireRole(allowedRoles ...string) func(http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\trole, ok := r.Context().Value(UserRoleKey).(string)\n\t\t\tif !ok {\n\t\t\t\thttp.Error(w, \"unauthorized\", http.StatusUnauthorized)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Check if user has one of the allowed roles\n\t\t\thasRole := false\n\t\t\tfor _, allowedRole := range allowedRoles {\n\t\t\t\tif role == allowedRole {\n\t\t\t\t\thasRole = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !hasRole {\n\t\t\t\thttp.Error(w, \"forbidden\", http.StatusForbidden)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/mw/auth.go\nType: func\nName: RequireVerified\nDoc:\nRequireVerified middleware - only allows verified_email and admin\n\nCode:\nfunc RequireVerified(next http.Handler) http.Handler {\n\treturn RequireRole(\"verified_email\", \"admin\")(next)\n}\n", + "File: /home/popertots/Crussell/backend/mw/auth.go\nType: func\nName: RequireAdmin\nDoc:\nRequireAdmin middleware - only allows admin\n\nCode:\nfunc RequireAdmin(next http.Handler) http.Handler {\n\treturn RequireRole(\"admin\")(next)\n}\n", + "File: /home/popertots/Crussell/backend/mw/auth.go\nType: func\nName: GetUserID\nDoc:\nHelper functions to get user info from context\n\nCode:\nfunc GetUserID(ctx context.Context) (string, bool) {\n\tuserID, ok := ctx.Value(UserIDKey).(string)\n\treturn userID, ok\n}\n", + "File: /home/popertots/Crussell/backend/mw/auth.go\nType: func\nName: GetUserRole\nCode:\nfunc GetUserRole(ctx context.Context) (string, bool) {\n\trole, ok := ctx.Value(UserRoleKey).(string)\n\treturn role, ok\n}\n", + "File: /home/popertots/Crussell/backend/db/db.go\nType: var\nName: DB\nCode:\nvar DB *pgxpool.Pool\n", + "File: /home/popertots/Crussell/backend/db/db.go\nType: func\nName: Connect\nCode:\nfunc Connect() error {\n\t// Connect to Postgres on local network (127.x.x.x)\n\tdsn := fmt.Sprintf(\n\t\t\"postgres://%s:%s@%s:5432/%s\",\n\t\tgetEnv(\"POSTGRES_USER\"),\n\t\tgetEnv(\"POSTGRES_PASSWORD\"),\n\t\tgetEnv(\"POSTGRES_HOST\"),\n\t\tgetEnv(\"POSTGRES_DB\"),\n\t)\n\n\tpool, err := pgxpool.New(context.Background(), dsn)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tDB = pool\n\n\terr = testDB()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n", + "File: /home/popertots/Crussell/backend/db/db.go\nType: func\nName: testDB\nCode:\nfunc testDB() error {\n\tctx := context.Background()\n\tconn, err := DB.Acquire(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer conn.Release()\n\n\trow := conn.QueryRow(ctx, \"SELECT 1\")\n\tvar result int\n\terr = row.Scan(&result)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n", + "File: /home/popertots/Crussell/backend/db/db.go\nType: func\nName: getEnv\nCode:\nfunc getEnv(key string) string {\n\tif val := os.Getenv(key); val != \"\" {\n\t\treturn val\n\t}\n\tlog.Fatal(\"FATAL: Environment variable not set:\", key)\n\treturn \"\"\n}\n", + "File: /home/popertots/Crussell/backend/db/db_dev.go\nType: var\nName: DB\nCode:\nvar DB *pgxpool.Pool\n", + "File: /home/popertots/Crussell/backend/db/db_dev.go\nType: func\nName: Connect\nCode:\nfunc Connect() error {\n\t// Connect to Postgres inside Docker network\n\tdsn := fmt.Sprintf(\n\t\t\"postgres://%s:%s@localhost:5432/%s?sslmode=disable\",\n\t\tgetEnv(\"POSTGRES_USER\"),\n\t\tgetEnv(\"POSTGRES_PASSWORD\"),\n\t\tgetEnv(\"POSTGRES_DB\"),\n\t)\n\n\tpool, err := pgxpool.New(context.Background(), dsn)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tDB = pool\n\n\terr = testDB()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n", + "File: /home/popertots/Crussell/backend/db/db_dev.go\nType: func\nName: testDB\nCode:\nfunc testDB() error {\n\tctx := context.Background()\n\tconn, err := DB.Acquire(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer conn.Release()\n\n\trow := conn.QueryRow(ctx, \"SELECT 1\")\n\tvar result int\n\terr = row.Scan(&result)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n", + "File: /home/popertots/Crussell/backend/db/db_dev.go\nType: func\nName: getEnv\nCode:\nfunc getEnv(key string) string {\n\tif val := os.Getenv(key); val != \"\" {\n\t\treturn val\n\t}\n\tlog.Fatal(\"FATAL: Environment variable not set:\", key)\n\treturn \"\"\n}\n", + "File: /home/popertots/Crussell/backend/auth/password.go\nLanguage: go\n\npackage auth", + "File: /home/popertots/Crussell/backend/auth/jwt.go\nType: var\nName: TokenAuth\nCode:\nvar TokenAuth *jwtauth.JWTAuth\n", + "File: /home/popertots/Crussell/backend/auth/jwt.go\nType: struct\nName: AuthResponse\nDoc:\nAuthResponse is the response structure for login/refresh endpoints\n\nCode:\ntype AuthResponse struct {\n\tToken string `json:\"token\"`\n}\n", + "File: /home/popertots/Crussell/backend/auth/jwt.go\nType: func\nName: InitJWT\nDoc:\nAuthResponse is the response structure for login/refresh endpoints\n\nCode:\nfunc InitJWT(secret string) {\n\tTokenAuth = jwtauth.New(\"HS256\", []byte(secret), nil)\n}\n", + "File: /home/popertots/Crussell/backend/auth/jwt.go\nType: func\nName: GenerateToken\nDoc:\nGenerateToken creates a JWT with user_id and role\n\nCode:\nfunc GenerateToken(userID string, role string) (string, error) {\n\t_, tokenString, err := TokenAuth.Encode(map[string]interface{}{\n\t\t\"user_id\": userID,\n\t\t\"role\": role,\n\t\t\"exp\": time.Now().Add(30 * 24 * time.Hour).Unix(), // 30 days\n\t})\n\treturn tokenString, err\n}\n", + "File: /home/popertots/Crussell/backend/auth/jwt.go\nType: func\nName: VerifyToken\nDoc:\nVerifyToken validates JWT and returns user_id and role\n\nCode:\nfunc VerifyToken(tokenString string, ctx context.Context) (userID string, role string, err error) {\n\ttoken, err := TokenAuth.Decode(tokenString)\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\n\tclaims, err := token.AsMap(ctx)\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\n\tuserID, ok := claims[\"user_id\"].(string)\n\tif !ok {\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid user_id claim\")\n\t}\n\n\trole, ok = claims[\"role\"].(string)\n\tif !ok {\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid role claim\")\n\t}\n\n\treturn userID, role, nil\n}\n", + "File: /home/popertots/Crussell/backend/handlers/services/services.go\nType: struct\nName: Service\nDoc:\nService represents a service in the system\n\nCode:\ntype Service struct {\n\tID string `json:\"id\"`\n\tName string `json:\"name\"`\n\tDescription string `json:\"description\"`\n\tPrice float64 `json:\"price\"`\n\tDurationMinutes int `json:\"duration_minutes\"`\n\tIsActive bool `json:\"is_active\"`\n\tPatchTestDurationHours int `json:\"patch_test_duration_hours\"`\n\tMinimumAgeRequired int `json:\"minimum_age_required\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n\tCreatedBy *string `json:\"created_by,omitempty\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/services/services.go\nType: struct\nName: ServiceResponse\nCode:\ntype ServiceResponse struct {\n\tID string `json:\"id\"`\n\tName string `json:\"name\"`\n\tDescription string `json:\"description\"`\n\tPrice float64 `json:\"price\"`\n\tDurationMinutes int `json:\"duration_minutes\"`\n\tPatchTestDurationHours int `json:\"patch_test_duration_hours\"`\n\tMinimumAgeRequired int `json:\"minimum_age_required\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/services/services.go\nType: struct\nName: CreateServiceRequest\nDoc:\nCreateServiceRequest represents the request payload for creating a new service\n\nCode:\ntype CreateServiceRequest struct {\n\tName string `json:\"name\" validate:\"required,min=1,max=100\"`\n\tDescription *string `json:\"description,omitempty\"`\n\tPrice float64 `json:\"price\" validate:\"required,gt=0\"`\n\tDurationMinutes int `json:\"duration_minutes\" validate:\"required,gt=0\"`\n\tPatchTestDurationHours int `json:\"patch_test_duration_hours\" validate:\"gte=0\"`\n\tMinimumAgeRequired int `json:\"minimum_age_required\" validate:\"gte=0,lte=100\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/services/services.go\nType: func\nName: ToggleService\nDoc:\nToggleServiceHandler handles toggling a service's active status\n\nCode:\nfunc ToggleService(w http.ResponseWriter, r *http.Request) {\n\tserviceID := chi.URLParam(r, \"id\")\n\tif serviceID == \"\" {\n\t\thttp.Error(w, \"Service ID is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tquery := \"UPDATE services SET is_active = NOT is_active WHERE id = $1\"\n\tresult, err := db.DB.Exec(r.Context(), query, serviceID)\n\tif err != nil {\n\t\thttp.Error(w, \"Failed to toggle service: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif result.RowsAffected() == 0 {\n\t\thttp.Error(w, \"Service not found\", http.StatusNotFound)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusOK)\n\tjson.NewEncoder(w).Encode(map[string]interface{}{\n\t\t\"message\": \"Service toggled successfully\",\n\t\t\"id\": serviceID,\n\t})\n}\n", + "File: /home/popertots/Crussell/backend/handlers/services/services.go\nType: func\nName: CreateServiceHandler\nDoc:\nPOST /api/admin/services\n\nCode:\nfunc CreateServiceHandler(w http.ResponseWriter, r *http.Request) {\n\t// Parse and validate request\n\tvar req CreateServiceRequest\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\thttp.Error(w, \"Invalid JSON: \"+err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Basic validation\n\tif req.Name == \"\" {\n\t\thttp.Error(w, \"Name is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif req.Price <= 0 {\n\t\thttp.Error(w, \"Price must be greater than 0\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif req.PatchTestDurationHours < 0 {\n\t\thttp.Error(w, \"Patch test duration cannot be negative\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif req.MinimumAgeRequired < 0 || req.MinimumAgeRequired > 100 {\n\t\thttp.Error(w, \"Minimum age must be between 0 and 100\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Get user ID from context (if authentication is added later)\n\tvar createdBy *string\n\tif userID, ok := r.Context().Value(mw.UserIDKey).(string); ok {\n\t\tcreatedBy = &userID\n\t}\n\n\t// Insert new service\n\tquery := `\n\t\tINSERT INTO services (\n\t\t\tname, description, price, duration_minutes, \n\t\t\tpatch_test_duration_hours, minimum_age_required, created_by\n\t\t) \n\t\tVALUES ($1, $2, $3, $4, $5, $6, $7)\n\t\tRETURNING \n\t\t\tid, name, description, price, duration_minutes, is_active,\n\t\t\tpatch_test_duration_hours, minimum_age_required, created_at, \n\t\t\tcreated_by\n\t`\n\n\tvar service Service\n\tvar createdByDB sql.NullString\n\n\terr := db.DB.QueryRow(r.Context(),\n\t\tquery,\n\t\treq.Name,\n\t\treq.Description,\n\t\treq.Price,\n\t\treq.DurationMinutes,\n\t\treq.PatchTestDurationHours,\n\t\treq.MinimumAgeRequired,\n\t\tcreatedBy,\n\t).Scan(\n\t\t&service.ID,\n\t\t&service.Name,\n\t\t&service.Description,\n\t\t&service.Price,\n\t\t&service.DurationMinutes,\n\t\t&service.IsActive,\n\t\t&service.PatchTestDurationHours,\n\t\t&service.MinimumAgeRequired,\n\t\t&service.CreatedAt,\n\t\t&createdByDB,\n\t)\n\n\tif err != nil {\n\t\t// Check for duplicate name or other constraints\n\t\tif err.Error() == \"pq: duplicate key value violates unique constraint\" {\n\t\t\thttp.Error(w, \"A service with this name already exists\", http.StatusConflict)\n\t\t\treturn\n\t\t}\n\t\thttp.Error(w, \"Failed to create service: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Convert nullable fields to pointers\n\tif createdByDB.Valid {\n\t\tservice.CreatedBy = &createdByDB.String\n\t}\n\n\t// Return created service\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusCreated)\n\tif err := json.NewEncoder(w).Encode(service); err != nil {\n\t\thttp.Error(w, \"Failed to encode response: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/services/services.go\nType: func\nName: DeleteServiceHandler\nDoc:\nDeleteServiceHandler handles soft deleting a service (setting is_active to false)\n\nCode:\nfunc DeleteServiceHandler(w http.ResponseWriter, r *http.Request) {\n\tserviceID := chi.URLParam(r, \"id\")\n\tif serviceID == \"\" {\n\t\thttp.Error(w, \"Service ID is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tquery := \"DELETE FROM services WHERE id = $1\"\n\tresult, err := db.DB.Exec(r.Context(), query, serviceID)\n\tif err != nil {\n\t\thttp.Error(w, \"Failed to delete service: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif result.RowsAffected() == 0 {\n\t\thttp.Error(w, \"Service not found\", http.StatusNotFound)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusOK)\n\tjson.NewEncoder(w).Encode(map[string]interface{}{\n\t\t\"message\": \"Service deleted successfully\",\n\t\t\"id\": serviceID,\n\t})\n}\n", + "File: /home/popertots/Crussell/backend/handlers/services/services.go\nType: func\nName: ServicesHandler\nDoc:\nServicesHandler returns all services from the database\n\nCode:\nfunc ServicesHandler(w http.ResponseWriter, r *http.Request) {\n\t// Query all active services\n\tquery := `\n\t\tSELECT id, name, description, price, duration_minutes, \n\t\t patch_test_duration_hours, minimum_age_required\n\t\tFROM services\n\t\tWHERE is_active = TRUE\n\t\tORDER BY name\n\t`\n\n\trows, err := db.DB.Query(r.Context(), query)\n\tif err != nil {\n\t\thttp.Error(w, \"Failed to fetch services: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\n\tvar services []ServiceResponse\n\n\tfor rows.Next() {\n\t\tvar service ServiceResponse\n\n\t\terr := rows.Scan(\n\t\t\t&service.ID,\n\t\t\t&service.Name,\n\t\t\t&service.Description,\n\t\t\t&service.Price,\n\t\t\t&service.DurationMinutes,\n\t\t\t&service.PatchTestDurationHours,\n\t\t\t&service.MinimumAgeRequired,\n\t\t)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"Failed to read service data: \"+err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tservices = append(services, service)\n\t}\n\n\tif err = rows.Err(); err != nil {\n\t\thttp.Error(w, \"Error iterating over services: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Set response headers\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusOK)\n\n\t// Return empty array instead of null if no services found\n\tif services == nil {\n\t\tservices = []ServiceResponse{}\n\t}\n\n\t// Encode response\n\tif err := json.NewEncoder(w).Encode(services); err != nil {\n\t\thttp.Error(w, \"Failed to encode response: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/services/services.go\nType: func\nName: AllServicesHandler\nDoc:\nAllServicesHandler returns all services including inactive ones (useful for admin)\n\nCode:\nfunc AllServicesHandler(w http.ResponseWriter, r *http.Request) {\n\t// Query all services including inactive ones\n\tquery := `\n\t\tSELECT id, name, description, price, duration_minutes, is_active, \n\t\t patch_test_duration_hours, minimum_age_required, created_at, created_by\n\t\tFROM services\n\t\tORDER BY is_active DESC, name\n\t`\n\n\trows, err := db.DB.Query(r.Context(), query)\n\tif err != nil {\n\t\thttp.Error(w, \"Failed to fetch services: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\n\tvar services []Service\n\n\tfor rows.Next() {\n\t\tvar service Service\n\t\tvar createdBy sql.NullString\n\n\t\terr := rows.Scan(\n\t\t\t&service.ID,\n\t\t\t&service.Name,\n\t\t\t&service.Description,\n\t\t\t&service.Price,\n\t\t\t&service.DurationMinutes,\n\t\t\t&service.IsActive,\n\t\t\t&service.PatchTestDurationHours,\n\t\t\t&service.MinimumAgeRequired,\n\t\t\t&service.CreatedAt,\n\t\t\t&createdBy,\n\t\t)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"Failed to read service data: \"+err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\t// Convert nullable fields to pointers\n\t\tif createdBy.Valid {\n\t\t\tservice.CreatedBy = &createdBy.String\n\t\t}\n\n\t\tservices = append(services, service)\n\t}\n\n\tif err = rows.Err(); err != nil {\n\t\thttp.Error(w, \"Error iterating over services: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Set response headers\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusOK)\n\n\t// Return empty array instead of null if no services found\n\tif services == nil {\n\t\tservices = []Service{}\n\t}\n\n\t// Encode response\n\tif err := json.NewEncoder(w).Encode(services); err != nil {\n\t\thttp.Error(w, \"Failed to encode response: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/user/profile.go\nType: var\nName: titleCaser\nCode:\nvar titleCaser = cases.Title(language.English)\n", + "File: /home/popertots/Crussell/backend/handlers/user/profile.go\nType: struct\nName: UserProfile\nCode:\ntype UserProfile struct {\n\tID string `json:\"id\"`\n\tEmail string `json:\"email\"`\n\tFirstName string `json:\"firstName\"`\n\tLastName string `json:\"lastName\"`\n\tPhone *string `json:\"phone,omitempty\"`\n\tDateOfBirth *string `json:\"dateOfBirth,omitempty\"`\n\tRole string `json:\"role\"`\n\tLoyaltyStamps int `json:\"loyaltyStamps\"`\n\tReferralCode string `json:\"referralCode\"`\n\tProfilePicURL *string `json:\"profilePicUrl,omitempty\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/user/profile.go\nType: struct\nName: UpdateProfileRequest\nCode:\ntype UpdateProfileRequest struct {\n\tFirstName string `json:\"firstName\"`\n\tLastName string `json:\"lastName\"`\n\tPhone string `json:\"phone\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/user/profile.go\nType: func\nName: GetProfileHandler\nDoc:\nGET /api/user/profile\n\nCode:\nfunc GetProfileHandler(w http.ResponseWriter, r *http.Request) {\n\tuserID, ok := mw.GetUserID(r.Context())\n\tif !ok {\n\t\thttp.Error(w, \"unauthorized\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\tvar user UserProfile\n\terr := db.DB.QueryRow(r.Context(), `\n\t\tSELECT \n\t\t\tid, email, n_first_name, n_last_name, phone,\n\t\t\tdate_of_birth::text, account_role, loyalty_stamps,\n\t\t\treferral_code, profile_pic_url\n\t\tFROM users \n\t\tWHERE id = $1\n\t`, userID).Scan(\n\t\t&user.ID, &user.Email, &user.FirstName, &user.LastName,\n\t\t&user.Phone, &user.DateOfBirth, &user.Role,\n\t\t&user.LoyaltyStamps, &user.ReferralCode, &user.ProfilePicURL,\n\t)\n\n\tif err != nil {\n\t\thttp.Error(w, \"user not found\", http.StatusNotFound)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(user)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/user/profile.go\nType: func\nName: updateCardDAV\nDoc:\nPUT /api/user/profile\nupdateCardDAV updates an existing contact in SabreDAV using user ID\n\nCode:\nfunc updateCardDAV(userID, firstName, lastName, email, phone, dob string) error {\n\t// Use user ID as filename - consistent with registration\n\tfilename := fmt.Sprintf(\"%s.vcf\", userID)\n\turl := fmt.Sprintf(\"http://nginx/dav/addressbooks/principals/default/default/%s\", filename)\n\n\t// Create vCard with user ID as UID (no need to fetch existing)\n\ttimestamp := time.Now().UTC().Format(\"20060102T150405Z\")\n\tuid := fmt.Sprintf(\"%s@example.com\", userID)\n\n\tvcard := fmt.Sprintf(`BEGIN:VCARD\nVERSION:3.0\nUID:%s\nFN:%s %s\nN:%s;%s;;;\nEMAIL;TYPE=INTERNET:%s\nTEL;TYPE=CELL:%s\nBDAY:%s\nREV:%s\nEND:VCARD`, uid, firstName, lastName, lastName, firstName, email, phone, dob, timestamp)\n\n\t// PUT updated vCard\n\treq, err := http.NewRequest(\"PUT\", url, bytes.NewBufferString(vcard))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create request: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"text/vcard; charset=utf-8\")\n\treq.SetBasicAuth(\"admin\", \"admin\")\n\n\tclient := &http.Client{Timeout: 10 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update CardDAV: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\treturn fmt.Errorf(\"CardDAV returned status: %d\", resp.StatusCode)\n\t}\n\n\treturn nil\n}\n", + "File: /home/popertots/Crussell/backend/handlers/user/profile.go\nType: func\nName: UpdateProfileHandler\nDoc:\nPUT /api/user/profile\n\nCode:\nfunc UpdateProfileHandler(w http.ResponseWriter, r *http.Request) {\n\tuserID, _ := mw.GetUserID(r.Context())\n\n\tvar req UpdateProfileRequest\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\thttp.Error(w, \"invalid request\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Normalize input\n\treq.FirstName = strings.TrimSpace(req.FirstName)\n\treq.LastName = strings.TrimSpace(req.LastName)\n\treq.Phone = strings.TrimSpace(req.Phone)\n\n\t// Required fields\n\tif req.FirstName == \"\" || req.LastName == \"\" || req.Phone == \"\" {\n\t\thttp.Error(w, \"first name, last name and phone are required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Validate names (unicode letters, spaces, hyphen, apostrophe, dot)\n\tnameRegex := regexp.MustCompile(`^[\\p{L}\\p{M}\\s\\-'\\.]+$`)\n\n\tif !nameRegex.MatchString(req.FirstName) || !nameRegex.MatchString(req.LastName) {\n\t\thttp.Error(w, \"invalid characters in name\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Validate lengths\n\tif len(req.FirstName) < 1 || len(req.FirstName) > 50 {\n\t\thttp.Error(w, \"first name must be 1-50 characters\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif len(req.LastName) < 1 || len(req.LastName) > 50 {\n\t\thttp.Error(w, \"last name must be 1-50 characters\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Normalize phone (strip spaces, hyphens, brackets)\n\treq.Phone = strings.Map(func(r rune) rune {\n\t\tif (r >= '0' && r <= '9') || r == '+' {\n\t\t\treturn r\n\t\t}\n\t\treturn -1\n\t}, req.Phone)\n\n\t// Validate UK phone number\n\tphone, err := auth.ValidateUKPhoneNumber(req.Phone)\n\tif err != nil {\n\t\thttp.Error(w, \"invalid phone number format\", http.StatusBadRequest)\n\t\treturn\n\t}\n\treq.Phone = strings.TrimSpace(phone)\n\n\t// Title case names\n\treq.FirstName = titleCaser.String(strings.ToLower(req.FirstName))\n\treq.LastName = titleCaser.String(strings.ToLower(req.LastName))\n\n\t// Fetch user's email and DOB for CardDAV update\n\tvar email string\n\tvar dob time.Time\n\terr = db.DB.QueryRow(r.Context(), `\n\t\tSELECT email, date_of_birth FROM users WHERE id = $1\n\t`, userID).Scan(&email, &dob)\n\n\tif err != nil {\n\t\thttp.Error(w, \"failed to fetch user data\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Update DB\n\t_, err = db.DB.Exec(r.Context(), `\n\t\tUPDATE users \n\t\tSET n_first_name = $1, n_last_name = $2, phone = $3, updated_at = NOW()\n\t\tWHERE id = $4\n\t`, req.FirstName, req.LastName, req.Phone, userID)\n\n\tif err != nil {\n\t\thttp.Error(w, \"update failed\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Update CardDAV (non-blocking)\n\tgo func() {\n\t\tdobStr := dob.Format(\"2006-01-02\")\n\t\tif err := updateCardDAV(userID, req.FirstName, req.LastName, email, req.Phone, dobStr); err != nil {\n\t\t\tfmt.Printf(\"Warning: Failed to update CardDAV contact for user %s: %v\\n\", userID, err)\n\t\t}\n\t}()\n\n\tw.WriteHeader(http.StatusOK)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/user/loyalty.go\nType: struct\nName: LoyaltyResponse\nCode:\ntype LoyaltyResponse struct {\n\tStamps int `json:\"stamps\"`\n\tReferralCode string `json:\"referralCode\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/user/loyalty.go\nType: func\nName: GetLoyaltyHandler\nDoc:\nGET /api/user/loyalty\n\nCode:\nfunc GetLoyaltyHandler(w http.ResponseWriter, r *http.Request) {\n\tuserID, _ := mw.GetUserID(r.Context())\n\n\tvar loyalty LoyaltyResponse\n\terr := db.DB.QueryRow(r.Context(), `\n\t\tSELECT loyalty_stamps, referral_code\n\t\tFROM users\n\t\tWHERE id = $1\n\t`, userID).Scan(&loyalty.Stamps, &loyalty.ReferralCode)\n\n\tif err != nil {\n\t\thttp.Error(w, \"failed to get loyalty info\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(loyalty)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/user/account.go\nType: func\nName: DeleteAccountHandler\nDoc:\nDELETE /api/user/account\n\nCode:\nfunc DeleteAccountHandler(w http.ResponseWriter, r *http.Request) {\n\t// TODO: Delete user's data\n\n\tw.WriteHeader(http.StatusNoContent)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: var\nName: londonLocation\nCode:\nvar londonLocation = func() *time.Location {\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: Booking\nDoc:\nBooking represents a booking in the system\n\nCode:\ntype Booking struct {\n\tID string `json:\"id\"`\n\tStartTime time.Time `json:\"start_time\"`\n\tStatus string `json:\"status\"`\n\tNotes *string `json:\"notes,omitempty\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n\tUpdatedAt time.Time `json:\"updated_at\"`\n\tCreatedBy *string `json:\"created_by,omitempty\"`\n\n\t// Joined fields\n\tUser *UserSummary `json:\"user,omitempty\"`\n\tServices []BookingService `json:\"services,omitempty\"`\n\tPayments []Payment `json:\"payments,omitempty\"`\n\tTotalAmount float64 `json:\"total_amount\"`\n\tAmountPaid float64 `json:\"amount_paid\"`\n\tAmountDue float64 `json:\"amount_due\"`\n\tDurationMinutes int `json:\"duration_minutes\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: BookingService\nDoc:\nBookingService represents a service associated with a booking\n\nCode:\ntype BookingService struct {\n\tBookingID string `json:\"booking_id\"`\n\tServiceID string `json:\"service_id\"`\n\tOverridePrice *float64 `json:\"override_price,omitempty\"`\n\tOverrideDurationMinutes *int `json:\"override_duration_minutes,omitempty\"`\n\n\t// Service details (joined)\n\tServiceName *string `json:\"service_name,omitempty\"`\n\tServiceDescription *string `json:\"service_description,omitempty\"`\n\tPrice *float64 `json:\"price,omitempty\"`\n\tDurationMinutes *int `json:\"duration_minutes,omitempty\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: Payment\nDoc:\nPayment represents a payment associated with a booking\n\nCode:\ntype Payment struct {\n\tID string `json:\"id\"`\n\tBookingID string `json:\"booking_id\"`\n\tPaymentType string `json:\"payment_type\"`\n\tPaymentMethod string `json:\"payment_method\"`\n\tVendorCode *string `json:\"vendor_code,omitempty\"`\n\tInvoiceNumber *int `json:\"invoice_number,omitempty\"`\n\tStatus string `json:\"status\"`\n\tAmount float64 `json:\"amount\"`\n\tIsVATApplicable bool `json:\"is_vat_applicable\"`\n\tVATRate *float64 `json:\"vat_rate,omitempty\"`\n\tVATAmount *float64 `json:\"vat_amount,omitempty\"`\n\tNetAmount *float64 `json:\"net_amount,omitempty\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n\tUpdatedAt time.Time `json:\"updated_at\"`\n\tCreatedBy *string `json:\"created_by,omitempty\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: CreateBookingRequest\nDoc:\nCreateBookingRequest represents the request payload for creating a new booking\n\nCode:\ntype CreateBookingRequest struct {\n\tStartTime time.Time `json:\"start_time\" validate:\"required\"`\n\tServiceIDs []string `json:\"service_ids\" validate:\"required,min=1\"`\n\tNotes *string `json:\"notes,omitempty\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: EditBookingRequest\nDoc:\nEditBookingRequest represents the request payload for editing a booking's start time\n\nCode:\ntype EditBookingRequest struct {\n\tStartTime time.Time `json:\"start_time\" validate:\"required\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: ProgressBookingRequest\nDoc:\nProgressBookingRequest represents the request payload for updating a booking's status\n\nCode:\ntype ProgressBookingRequest struct {\n\tStatus string `json:\"status\" validate:\"required,oneof=pending confirmed in_progress completed client_cancelled we_cancelled re-schedule no_show\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: ConfirmBookingRequest\nDoc:\nConfirmBookingRequest represents the request payload for confirming a booking\n\nCode:\ntype ConfirmBookingRequest struct {\n\tServiceOverrides []ServiceOverride `json:\"service_overrides,omitempty\"`\n\tNotes *string `json:\"notes,omitempty\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: ServiceOverride\nDoc:\nServiceOverride represents override values for a specific service in a booking\n\nCode:\ntype ServiceOverride struct {\n\tServiceID string `json:\"service_id\" validate:\"required\"`\n\tOverridePrice *float64 `json:\"override_price,omitempty\"`\n\tOverrideDurationMinutes *int `json:\"override_duration_minutes,omitempty\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: DeleteBookingRequest\nDoc:\nDeleteBookingRequest represents the request payload for deleting a booking with payment\n\nCode:\ntype DeleteBookingRequest struct {\n\tReason string `json:\"reason\" validate:\"required,oneof=client_cancelled we_cancelled re-schedule no_show\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: AdminUserSummary\nDoc:\nAdminUserSummary represents a small user summary for admin views\n\nCode:\ntype AdminUserSummary struct {\n\tFullName string `json:\"full_name\"`\n\tProfilePicURL *string `json:\"profile_pic_url,omitempty\"`\n\tNotes *string `json:\"notes,omitempty\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: AdminBookingSummary\nDoc:\nAdminBookingSummary represents a complete booking summary for admin view\n\nCode:\ntype AdminBookingSummary struct {\n\tBooking Booking `json:\"booking\"`\n\tUser *AdminUserSummary `json:\"user,omitempty\"`\n\tServices []BookingServiceDetail `json:\"services\"`\n\tPayments []Payment `json:\"payments\"`\n\tTotalAmount float64 `json:\"total_amount\"`\n\tAmountPaid float64 `json:\"amount_paid\"`\n\tAmountDue float64 `json:\"amount_due\"`\n\tDurationMinutes int `json:\"duration_minutes\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: UserSummary\nCode:\ntype UserSummary struct {\n\tID string `json:\"id\"`\n\tFirstName string `json:\"first_name\"`\n\tLastName string `json:\"last_name\"`\n\tFullName string `json:\"full_name\"`\n\tEmail *string `json:\"email,omitempty\"`\n\tPhone *string `json:\"phone,omitempty\"`\n\tProfilePicURL *string `json:\"profile_pic_url,omitempty\"`\n\tDateOfBirth *string `json:\"date_of_birth,omitempty\"`\n\tAccountRole string `json:\"account_role\"`\n\tLoyaltyStamps *int `json:\"loyalty_stamps,omitempty\"`\n\tReferralCode *string `json:\"referral_code,omitempty\"`\n\tReferralCodeUses *int `json:\"referral_code_uses,omitempty\"`\n\tCreatedAt string `json:\"created_at\"`\n\tNotes *string `json:\"notes,omitempty\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: BookingServiceDetail\nCode:\ntype BookingServiceDetail struct {\n\tServiceName string `json:\"service_name\"`\n\tServiceDescription *string `json:\"service_description,omitempty\"`\n\tBasePrice float64 `json:\"base_price\"`\n\tBaseDurationMinutes int `json:\"base_duration_minutes\"`\n\tOverridePrice *float64 `json:\"override_price,omitempty\"`\n\tOverrideDurationMinutes *int `json:\"override_duration_minutes,omitempty\"`\n\tIsActive bool `json:\"is_active\"`\n\tRequiresPatchTest bool `json:\"requires_patch_test\"`\n\tMinimumAgeRequired int `json:\"minimum_age_required\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: GetAllBookingsRequest\nDoc:\nGetAllBookingsRequest represents query parameters for getting all bookings\n\nCode:\ntype GetAllBookingsRequest struct {\n\tStatus *string `json:\"status,omitempty\"`\n\tStartDate *string `json:\"start_date,omitempty\"`\n\tEndDate *string `json:\"end_date,omitempty\"`\n\tPage int `json:\"page\"`\n\tPerPage int `json:\"per_page\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: BookingListResponse\nDoc:\nBookingListResponse represents a paginated list of bookings\n\nCode:\ntype BookingListResponse struct {\n\tBookings []Booking `json:\"bookings\"`\n\tPage int `json:\"page\"`\n\tPerPage int `json:\"per_page\"`\n\tTotal int `json:\"total\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: SearchBookingsRequest\nDoc:\nSearchBookingsRequest represents search parameters\n\nCode:\ntype SearchBookingsRequest struct {\n\tQuery string `json:\"query\"`\n\tPage int `json:\"page\"`\n\tPerPage int `json:\"per_page\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: SearchBookingsResponse\nDoc:\nSearchBookingsResponse represents search results\n\nCode:\ntype SearchBookingsResponse struct {\n\tBookings []AdminBookingSummary `json:\"bookings\"`\n\tPage int `json:\"page\"`\n\tPerPage int `json:\"per_page\"`\n\tTotal int `json:\"total\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: UserBookingDetail\nDoc:\nEnhanced booking response for user endpoints\n\nCode:\ntype UserBookingDetail struct {\n\tBooking Booking `json:\"booking\"`\n\tTotalAmount float64 `json:\"total_amount\"`\n\tAmountPaid float64 `json:\"amount_paid\"`\n\tAmountDue float64 `json:\"amount_due\"`\n\tDurationMinutes int `json:\"duration_minutes\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: struct\nName: AdminBookingDetail\nDoc:\nEnhanced booking response for admin endpoints\n\nCode:\ntype AdminBookingDetail struct {\n\tBooking Booking `json:\"booking\"`\n\tUser *AdminUserSummary `json:\"user,omitempty\"`\n\tTotalAmount float64 `json:\"total_amount\"`\n\tAmountPaid float64 `json:\"amount_paid\"`\n\tAmountDue float64 `json:\"amount_due\"`\n\tDurationMinutes int `json:\"duration_minutes\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: parseGetAllBookingsRequest\nDoc:\nHelper function to parse query parameters\n\nCode:\nfunc parseGetAllBookingsRequest(r *http.Request) GetAllBookingsRequest {\n\treq := GetAllBookingsRequest{\n\t\tPage: 1,\n\t\tPerPage: 10, // default page size\n\t}\n\n\tif status := r.URL.Query().Get(\"status\"); status != \"\" {\n\t\treq.Status = &status\n\t}\n\n\tif startDate := r.URL.Query().Get(\"start_date\"); startDate != \"\" {\n\t\treq.StartDate = &startDate\n\t}\n\n\tif endDate := r.URL.Query().Get(\"end_date\"); endDate != \"\" {\n\t\treq.EndDate = &endDate\n\t}\n\n\tif pageStr := r.URL.Query().Get(\"page\"); pageStr != \"\" {\n\t\tif page, err := strconv.Atoi(pageStr); err == nil && page > 0 {\n\t\t\treq.Page = page\n\t\t}\n\t}\n\n\tif perPageStr := r.URL.Query().Get(\"per_page\"); perPageStr != \"\" {\n\t\tif perPage, err := strconv.Atoi(perPageStr); err == nil && perPage > 0 && perPage <= 100 {\n\t\t\treq.PerPage = perPage\n\t\t}\n\t}\n\n\treturn req\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: GetAllUserBookingsHandler\nDoc:\nGET /api/bookings\n\nCode:\nfunc GetAllUserBookingsHandler(w http.ResponseWriter, r *http.Request) {\n\tuserID, ok := r.Context().Value(mw.UserIDKey).(string)\n\tif !ok || userID == \"\" {\n\t\thttp.Error(w, \"Authentication required\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t// Parse query parameters\n\treq := parseGetAllBookingsRequest(r)\n\n\t// Build base query with user filter\n\tbaseQuery := `\n\t\tSELECT id, start_time, status, notes, created_at, updated_at, created_by\n\t\tFROM bookings\n\t\tWHERE user_id = $1\n\t`\n\n\tcountQuery := `SELECT COUNT(*) FROM bookings WHERE user_id = $1`\n\tvar args []interface{}\n\targs = append(args, userID)\n\tparamCount := 2\n\n\t// Add filters\n\tif req.Status != nil {\n\t\tbaseQuery += fmt.Sprintf(\" AND status = $%d\", paramCount)\n\t\tcountQuery += fmt.Sprintf(\" AND status = $%d\", paramCount)\n\t\targs = append(args, *req.Status)\n\t\tparamCount++\n\t}\n\n\tif req.StartDate != nil {\n\t\tbaseQuery += fmt.Sprintf(\" AND start_time >= $%d\", paramCount)\n\t\tcountQuery += fmt.Sprintf(\" AND start_time >= $%d\", paramCount)\n\t\tstartTime, err := time.ParseInLocation(\"2006-01-02\", *req.StartDate, londonLocation)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"Invalid start_date format, use YYYY-MM-DD\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\t// Ensure it's at start of day in London time\n\t\tstartTime = time.Date(startTime.Year(), startTime.Month(), startTime.Day(), 0, 0, 0, 0, londonLocation)\n\t\targs = append(args, startTime)\n\t\tparamCount++\n\t}\n\n\tif req.EndDate != nil {\n\t\tbaseQuery += fmt.Sprintf(\" AND start_time <= $%d\", paramCount)\n\t\tcountQuery += fmt.Sprintf(\" AND start_time <= $%d\", paramCount)\n\t\tendTime, err := time.Parse(\"2006-01-02\", *req.EndDate)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"Invalid end_date format, use YYYY-MM-DD\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\t// Add end of day\n\t\tendTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second)\n\t\targs = append(args, endTime)\n\t\tparamCount++\n\t}\n\n\t// Add ordering and pagination\n\tbaseQuery += \" ORDER BY start_time ASC\"\n\tif req.PerPage > 0 {\n\t\tbaseQuery += fmt.Sprintf(\" LIMIT $%d OFFSET $%d\", paramCount, paramCount+1)\n\t\targs = append(args, req.PerPage, (req.Page-1)*req.PerPage)\n\t}\n\n\t// Get total count\n\tvar total int\n\terr := db.DB.QueryRow(r.Context(), countQuery, args[:1]...).Scan(&total)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to get booking count for user %s: %v\", userID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Get bookings\n\trows, err := db.DB.Query(r.Context(), baseQuery, args...)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to fetch bookings for user %s: %v\", userID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\n\tvar bookings []Booking\n\tfor rows.Next() {\n\t\tvar b Booking\n\t\tvar createdBy sql.NullString\n\t\terr := rows.Scan(&b.ID, &b.StartTime, &b.Status, &b.Notes, &b.CreatedAt, &b.UpdatedAt, &createdBy)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Failed to scan booking row: %v\", err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tif createdBy.Valid {\n\t\t\tb.CreatedBy = &createdBy.String\n\t\t}\n\t\tbookings = append(bookings, b)\n\t}\n\n\tresponse := BookingListResponse{\n\t\tBookings: bookings,\n\t\tPage: req.Page,\n\t\tPerPage: req.PerPage,\n\t\tTotal: total,\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tif err := json.NewEncoder(w).Encode(response); err != nil {\n\t\tlog.Printf(\"Failed to encode response: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: GetAllAdminBookingsHandler\nDoc:\nGET /api/admin/bookings\n\nCode:\nfunc GetAllAdminBookingsHandler(w http.ResponseWriter, r *http.Request) {\n\t// Parse query parameters\n\treq := parseGetAllBookingsRequest(r)\n\n\t// Build base query - only what the component needs\n\tbaseQuery := `\n WITH booking_totals AS (\n SELECT \n bs.booking_id,\n SUM(COALESCE(bs.override_duration_minutes, s.duration_minutes)) as total_duration,\n SUM(COALESCE(bs.override_price, s.price)) as total_amount\n FROM booking_services bs\n LEFT JOIN services s ON bs.service_id = s.id\n GROUP BY bs.booking_id\n ),\n payment_totals AS (\n SELECT \n booking_id,\n SUM(amount) as total_paid\n FROM payments \n WHERE status = 'completed'\n GROUP BY booking_id\n )\n SELECT \n b.id, \n b.start_time, \n b.status,\n u.fn,\n COALESCE(bt.total_duration, 0) as duration_minutes,\n COALESCE(bt.total_amount, 0) - COALESCE(pt.total_paid, 0) as amount_due\n FROM bookings b\n LEFT JOIN users u ON b.user_id = u.id\n LEFT JOIN booking_totals bt ON b.id = bt.booking_id\n LEFT JOIN payment_totals pt ON b.id = pt.booking_id\n `\n\n\tcountQuery := `SELECT COUNT(*) FROM bookings b`\n\tvar args []interface{}\n\tparamCount := 1\n\n\t// Add filters\n\twhereAdded := false\n\tif req.Status != nil {\n\t\tbaseQuery += fmt.Sprintf(\" WHERE b.status = $%d\", paramCount)\n\t\tcountQuery += fmt.Sprintf(\" WHERE b.status = $%d\", paramCount)\n\t\targs = append(args, *req.Status)\n\t\tparamCount++\n\t\twhereAdded = true\n\t}\n\n\tif req.StartDate != nil {\n\t\tif whereAdded {\n\t\t\tbaseQuery += fmt.Sprintf(\" AND b.start_time >= $%d\", paramCount)\n\t\t\tcountQuery += fmt.Sprintf(\" AND b.start_time >= $%d\", paramCount)\n\t\t} else {\n\t\t\tbaseQuery += fmt.Sprintf(\" WHERE b.start_time >= $%d\", paramCount)\n\t\t\tcountQuery += fmt.Sprintf(\" WHERE b.start_time >= $%d\", paramCount)\n\t\t\twhereAdded = true\n\t\t}\n\t\tstartTime, err := time.Parse(\"2006-01-02\", *req.StartDate)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"Invalid start_date format, use YYYY-MM-DD\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\targs = append(args, startTime)\n\t\tparamCount++\n\t}\n\n\tif req.EndDate != nil {\n\t\tif whereAdded {\n\t\t\tbaseQuery += fmt.Sprintf(\" AND b.start_time <= $%d\", paramCount)\n\t\t\tcountQuery += fmt.Sprintf(\" AND b.start_time <= $%d\", paramCount)\n\t\t} else {\n\t\t\tbaseQuery += fmt.Sprintf(\" WHERE b.start_time <= $%d\", paramCount)\n\t\t\tcountQuery += fmt.Sprintf(\" WHERE b.start_time <= $%d\", paramCount)\n\t\t}\n\t\tendTime, err := time.Parse(\"2006-01-02\", *req.EndDate)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"Invalid end_date format, use YYYY-MM-DD\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\tendTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second)\n\t\targs = append(args, endTime)\n\t\tparamCount++\n\t}\n\n\t// Add ordering and pagination (NO GROUP BY needed here)\n\tbaseQuery += \" ORDER BY b.start_time ASC\"\n\tif req.PerPage > 0 {\n\t\tbaseQuery += fmt.Sprintf(\" LIMIT $%d OFFSET $%d\", paramCount, paramCount+1)\n\t\targs = append(args, req.PerPage, (req.Page-1)*req.PerPage)\n\t\tparamCount += 2\n\t}\n\n\t// Get total count\n\tcountArgs := args\n\tif req.PerPage > 0 {\n\t\tcountArgs = args[:len(args)-2]\n\t}\n\n\tvar total int\n\terr := db.DB.QueryRow(r.Context(), countQuery, countArgs...).Scan(&total)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to get total booking count: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Get bookings\n\trows, err := db.DB.Query(r.Context(), baseQuery, args...)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to fetch all bookings: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\n\tvar bookings []Booking\n\tbookingIDs := []string{}\n\n\tfor rows.Next() {\n\t\tvar b Booking\n\t\tvar userFullName string\n\n\t\terr := rows.Scan(&b.ID, &b.StartTime, &b.Status, &userFullName, &b.DurationMinutes, &b.AmountDue)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Failed to scan booking row: %v\", err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\t// Create minimal user with just full_name\n\t\tb.User = &UserSummary{\n\t\t\tFullName: userFullName,\n\t\t}\n\n\t\tbookings = append(bookings, b)\n\t\tbookingIDs = append(bookingIDs, b.ID)\n\t}\n\n\t// Fetch service names only\n\tif len(bookingIDs) > 0 {\n\t\tservicesQuery := `\n\t\t\tSELECT bs.booking_id, s.name\n\t\t\tFROM booking_services bs\n\t\t\tJOIN services s ON bs.service_id = s.id\n\t\t\tWHERE bs.booking_id = ANY($1)\n\t\t\tORDER BY bs.booking_id, s.name\n\t\t`\n\n\t\tserviceRows, err := db.DB.Query(r.Context(), servicesQuery, bookingIDs)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Failed to fetch booking services: %v\", err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tdefer serviceRows.Close()\n\n\t\tservicesByBooking := make(map[string][]BookingService)\n\n\t\tfor serviceRows.Next() {\n\t\t\tvar bookingID string\n\t\t\tvar serviceName string\n\n\t\t\tif err := serviceRows.Scan(&bookingID, &serviceName); err != nil {\n\t\t\t\tlog.Printf(\"Failed to scan service row: %v\", err)\n\t\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tservice := BookingService{\n\t\t\t\tBookingID: bookingID,\n\t\t\t\tServiceName: &serviceName,\n\t\t\t}\n\t\t\tservicesByBooking[bookingID] = append(servicesByBooking[bookingID], service)\n\t\t}\n\n\t\t// Assign services to each booking\n\t\tfor i := range bookings {\n\t\t\tif services, exists := servicesByBooking[bookings[i].ID]; exists {\n\t\t\t\tbookings[i].Services = services\n\t\t\t}\n\t\t}\n\t}\n\n\tresponse := BookingListResponse{\n\t\tBookings: bookings,\n\t\tPage: req.Page,\n\t\tPerPage: req.PerPage,\n\t\tTotal: total,\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tif err := json.NewEncoder(w).Encode(response); err != nil {\n\t\tlog.Printf(\"Failed to encode response: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: GetAllBookingsByUserHandler\nDoc:\nGET /api/admin/bookings/user/{user_id}\n\nCode:\nfunc GetAllBookingsByUserHandler(w http.ResponseWriter, r *http.Request) {\n\tuserID := chi.URLParam(r, \"user_id\")\n\tif userID == \"\" {\n\t\thttp.Error(w, \"User ID is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Parse query parameters\n\treq := parseGetAllBookingsRequest(r)\n\n\t// Build query with specific user filter\n\tbaseQuery := `\n\t\tSELECT id, user_id, start_time, status, notes, created_at, updated_at, created_by,\n\t\t u.fn, u.profile_pic_url, u.notes as user_notes\n\t\tFROM bookings b\n\t\tLEFT JOIN users u ON b.user_id = u.id\n\t\tWHERE b.user_id = $1\n\t`\n\n\tcountQuery := `SELECT COUNT(*) FROM bookings WHERE user_id = $1`\n\tvar args []interface{}\n\targs = append(args, userID)\n\tparamCount := 2\n\n\t// Add filters\n\tif req.Status != nil {\n\t\tbaseQuery += fmt.Sprintf(\" AND b.status = $%d\", paramCount)\n\t\tcountQuery += fmt.Sprintf(\" AND status = $%d\", paramCount)\n\t\targs = append(args, *req.Status)\n\t\tparamCount++\n\t}\n\n\tif req.StartDate != nil {\n\t\tbaseQuery += fmt.Sprintf(\" AND b.start_time >= $%d\", paramCount)\n\t\tcountQuery += fmt.Sprintf(\" AND start_time >= $%d\", paramCount)\n\t\tstartTime, err := time.ParseInLocation(\"2006-01-02\", *req.StartDate, londonLocation)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"Invalid start_date format, use YYYY-MM-DD\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\t// Ensure it's at start of day in London time\n\t\tstartTime = time.Date(startTime.Year(), startTime.Month(), startTime.Day(), 0, 0, 0, 0, londonLocation)\n\t\targs = append(args, startTime)\n\t\tparamCount++\n\t}\n\n\tif req.EndDate != nil {\n\t\tbaseQuery += fmt.Sprintf(\" AND b.start_time <= $%d\", paramCount)\n\t\tcountQuery += fmt.Sprintf(\" AND start_time <= $%d\", paramCount)\n\t\tendTime, err := time.Parse(\"2006-01-02\", *req.EndDate)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"Invalid end_date format, use YYYY-MM-DD\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\tendTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second)\n\t\targs = append(args, endTime)\n\t\tparamCount++\n\t}\n\n\t// Add ordering and pagination\n\tbaseQuery += \" ORDER BY b.start_time ASC\"\n\tif req.PerPage > 0 {\n\t\tbaseQuery += fmt.Sprintf(\" LIMIT $%d OFFSET $%d\", paramCount, paramCount+1)\n\t\targs = append(args, req.PerPage, (req.Page-1)*req.PerPage)\n\t}\n\n\t// Get total count\n\tvar total int\n\terr := db.DB.QueryRow(r.Context(), countQuery, args[:1]...).Scan(&total)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to get booking count for user %s: %v\", userID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Get bookings\n\trows, err := db.DB.Query(r.Context(), baseQuery, args...)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to fetch bookings for user %s: %v\", userID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\n\tvar bookings []Booking\n\tfor rows.Next() {\n\t\tvar b Booking\n\t\tb.User = &UserSummary{}\n\t\tvar createdBy sql.NullString\n\n\t\tvar userFullName, userPicURL, userNotes sql.NullString\n\t\terr := rows.Scan(\n\t\t\t&b.ID, &b.User.ID, &b.StartTime, &b.Status, &b.Notes,\n\t\t\t&b.CreatedAt, &b.UpdatedAt, &createdBy,\n\t\t\t&userFullName, &userPicURL, &userNotes,\n\t\t)\n\t\tif userFullName.Valid {\n\t\t\tb.User.FullName = userFullName.String\n\t\t}\n\t\tif userPicURL.Valid {\n\t\t\tb.User.ProfilePicURL = &userPicURL.String\n\t\t}\n\t\tif userNotes.Valid {\n\t\t\tb.User.Notes = &userNotes.String\n\t\t}\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Failed to scan booking row: %v\", err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tif createdBy.Valid {\n\t\t\tb.CreatedBy = &createdBy.String\n\t\t}\n\t\tbookings = append(bookings, b)\n\t}\n\n\tresponse := BookingListResponse{\n\t\tBookings: bookings,\n\t\tPage: req.Page,\n\t\tPerPage: req.PerPage,\n\t\tTotal: total,\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tif err := json.NewEncoder(w).Encode(response); err != nil {\n\t\tlog.Printf(\"Failed to encode response: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: GetAdminBookingHandler\nDoc:\nGET /api/admin/bookings/{id}\n\nCode:\nfunc GetAdminBookingHandler(w http.ResponseWriter, r *http.Request) {\n\tbookingID := chi.URLParam(r, \"id\")\n\tif bookingID == \"\" {\n\t\thttp.Error(w, \"Booking ID is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// ----------------------------\n\t// 1. Fetch booking + user\n\t// ----------------------------\n\tvar booking Booking\n\tbooking.User = &UserSummary{}\n\n\terr := db.DB.QueryRow(r.Context(), `\n\t\tSELECT \n\t\t\tb.id, b.user_id, b.start_time, b.status, b.notes,\n\t\t\tb.created_at, b.updated_at, b.created_by,\n\t\t\tu.fn, u.email, u.phone, u.profile_pic_url, u.loyalty_stamps, \n\t\t\tu.referral_code, u.notes\n\t\tFROM bookings b\n\t\tLEFT JOIN users u ON b.user_id = u.id\n\t\tWHERE b.id = $1\n\t`, bookingID).Scan(\n\t\t&booking.ID, &booking.User.ID, &booking.StartTime, &booking.Status, &booking.Notes,\n\t\t&booking.CreatedAt, &booking.UpdatedAt, &booking.CreatedBy,\n\t\t&booking.User.FullName, &booking.User.Email, &booking.User.Phone, &booking.User.ProfilePicURL, &booking.User.LoyaltyStamps,\n\t\t&booking.User.ReferralCode, &booking.User.Notes,\n\t)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\thttp.Error(w, \"Booking not found\", http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\t\tlog.Printf(\"Failed to fetch booking %s: %v\", bookingID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// ----------------------------\n\t// 1.5. Fetch referral code uses count\n\t// ----------------------------\n\tvar referralCodeUses int\n\terr = db.DB.QueryRow(r.Context(), `\n SELECT COUNT(*) \n FROM user_referrals \n WHERE referrer_id = $1\n`, booking.User.ID).Scan(&referralCodeUses)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to fetch referral code uses for user %s: %v\", booking.User.ID, err)\n\t\t// Don't fail the entire request, just log and continue with 0\n\t\treferralCodeUses = 0\n\t}\n\tbooking.User.ReferralCodeUses = &referralCodeUses\n\n\t// ----------------------------\n\t// 2. Fetch services and calculate totals\n\t// ----------------------------\n\tserviceRows, err := db.DB.Query(r.Context(), `\n\t\tSELECT \n\t\t\ts.name,\n\t\t\tcoalesce(bs.override_price, s.price) as price,\n\t\t\tcoalesce(bs.override_duration_minutes, s.duration_minutes) as duration_minutes\n\t\tFROM booking_services bs\n\t\tLEFT JOIN services s ON bs.service_id = s.id\n\t\tWHERE bs.booking_id = $1\n\t\tORDER BY s.name\n\t`, bookingID)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to fetch services for booking %s: %v\", bookingID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer serviceRows.Close()\n\n\tvar totalAmount float64\n\tvar durationMinutes int\n\n\tfor serviceRows.Next() {\n\t\tvar name string\n\t\tvar price float64\n\t\tvar durationMinutes int\n\n\t\tif err := serviceRows.Scan(&name, &price, &durationMinutes); err != nil {\n\t\t\tlog.Printf(\"Failed to scan service for booking %s: %v\", bookingID, err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\t// Calculate totals\n\t\ttotalAmount += price\n\t\tdurationMinutes += durationMinutes\n\t\tbooking.Services = append(booking.Services, BookingService{\n\t\t\tServiceName: &name,\n\t\t\tPrice: &price,\n\t\t\tDurationMinutes: &durationMinutes,\n\t\t})\n\t}\n\n\tbooking.TotalAmount = totalAmount\n\tbooking.DurationMinutes = durationMinutes\n\n\t// ----------------------------\n\t// 3. Fetch payments and calculate amount paid\n\t// ----------------------------\n\tpaymentRows, err := db.DB.Query(r.Context(), `\n\t\tSELECT\n\t\t\tpayment_type, payment_method, vendor_code, invoice_number,\n\t\t\tstatus, amount, created_at\n\t\tFROM payments\n\t\tWHERE booking_id = $1\n\t\tORDER BY created_at ASC\n\t`, bookingID)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to fetch payments for booking %s: %v\", bookingID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer paymentRows.Close()\n\n\tvar payments []Payment\n\tvar amountPaid float64\n\n\tfor paymentRows.Next() {\n\t\tvar p Payment\n\t\tvar vendorCode sql.NullString\n\t\tvar invoiceNumber sql.NullInt32\n\n\t\terr := paymentRows.Scan(\n\t\t\t&p.PaymentType, &p.PaymentMethod, &vendorCode, &invoiceNumber,\n\t\t\t&p.Status, &p.Amount, &p.CreatedAt,\n\t\t)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Failed to scan payment row for booking %s: %v\", bookingID, err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tif vendorCode.Valid && vendorCode.String != \"\" {\n\t\t\tp.VendorCode = &vendorCode.String\n\t\t}\n\t\tif invoiceNumber.Valid {\n\t\t\tnum := int(invoiceNumber.Int32)\n\t\t\tp.InvoiceNumber = &num\n\t\t}\n\n\t\tpayments = append(payments, p)\n\n\t\tif p.Status == \"completed\" {\n\t\t\tamountPaid += p.Amount\n\t\t}\n\t}\n\n\tif len(payments) > 0 {\n\t\tbooking.Payments = payments\n\t}\n\tbooking.AmountPaid = amountPaid\n\tbooking.AmountDue = totalAmount - amountPaid\n\n\t// ----------------------------\n\t// Return JSON response\n\t// ----------------------------\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusOK)\n\tif err := json.NewEncoder(w).Encode(booking); err != nil {\n\t\tlog.Printf(\"Failed to encode booking response: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: SearchAdminBookingsHandler\nDoc:\nGET /api/admin/bookings/search\n\nCode:\nfunc SearchAdminBookingsHandler(w http.ResponseWriter, r *http.Request) {\n\tquery := r.URL.Query().Get(\"q\")\n\tif query == \"\" {\n\t\thttp.Error(w, \"Search query 'q' is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Parse pagination\n\tpage := 1\n\tperPage := 10\n\tif pageStr := r.URL.Query().Get(\"page\"); pageStr != \"\" {\n\t\tif p, err := strconv.Atoi(pageStr); err == nil && p > 0 {\n\t\t\tpage = p\n\t\t}\n\t}\n\tif perPageStr := r.URL.Query().Get(\"per_page\"); perPageStr != \"\" {\n\t\tif pp, err := strconv.Atoi(perPageStr); err == nil && pp > 0 && pp <= 100 {\n\t\t\tperPage = pp\n\t\t}\n\t}\n\n\t// Escape the search query to prevent SQL injection in LIKE patterns\n\tescapedQuery := strings.ReplaceAll(query, `\\`, `\\\\`)\n\tescapedQuery = strings.ReplaceAll(escapedQuery, `%`, `\\%`)\n\tescapedQuery = strings.ReplaceAll(escapedQuery, `_`, `\\_`)\n\tsearchPattern := \"%\" + escapedQuery + \"%\"\n\n\t// Build the main search query using CTEs to match your actual schema\n\tsearchQuery := `\n WITH booking_totals AS (\n SELECT \n bs.booking_id,\n SUM(COALESCE(bs.override_duration_minutes, s.duration_minutes)) as total_duration,\n SUM(COALESCE(bs.override_price, s.price)) as total_amount\n FROM booking_services bs\n LEFT JOIN services s ON bs.service_id = s.id\n GROUP BY bs.booking_id\n ),\n payment_totals AS (\n SELECT \n booking_id,\n SUM(amount) as total_paid\n FROM payments \n WHERE status = 'completed'\n GROUP BY booking_id\n )\n SELECT \n b.id, \n b.start_time, \n b.status,\n u.fn as full_name,\n COALESCE(bt.total_duration, 0) as duration_minutes,\n COALESCE(bt.total_amount, 0) - COALESCE(pt.total_paid, 0) as amount_due\n FROM bookings b\n LEFT JOIN users u ON b.user_id = u.id\n LEFT JOIN booking_totals bt ON b.id = bt.booking_id\n LEFT JOIN payment_totals pt ON b.id = pt.booking_id\n WHERE \n b.id ILIKE $1 ESCAPE '\\' OR\n b.notes ILIKE $1 ESCAPE '\\' OR\n b.status::text ILIKE $1 ESCAPE '\\' OR\n u.n_first_name ILIKE $1 ESCAPE '\\' OR\n u.n_last_name ILIKE $1 ESCAPE '\\' OR\n u.fn ILIKE $1 ESCAPE '\\' OR\n u.email ILIKE $1 ESCAPE '\\' OR\n u.phone ILIKE $1 ESCAPE '\\' OR\n EXISTS (\n SELECT 1 FROM booking_services bs \n JOIN services s ON bs.service_id = s.id \n WHERE bs.booking_id = b.id AND s.name ILIKE $1 ESCAPE '\\'\n )\n ORDER BY b.start_time ASC\n LIMIT $2 OFFSET $3\n `\n\n\tcountQuery := `\n SELECT COUNT(DISTINCT b.id)\n FROM bookings b\n LEFT JOIN users u ON b.user_id = u.id\n LEFT JOIN booking_services bs ON b.id = bs.booking_id\n LEFT JOIN services s ON bs.service_id = s.id\n WHERE \n b.id ILIKE $1 ESCAPE '\\' OR\n b.notes ILIKE $1 ESCAPE '\\' OR\n b.status::text ILIKE $1 ESCAPE '\\' OR\n u.n_first_name ILIKE $1 ESCAPE '\\' OR\n u.n_last_name ILIKE $1 ESCAPE '\\' OR\n u.fn ILIKE $1 ESCAPE '\\' OR\n u.email ILIKE $1 ESCAPE '\\' OR\n u.phone ILIKE $1 ESCAPE '\\' OR\n s.name ILIKE $1 ESCAPE '\\'\n `\n\n\toffset := (page - 1) * perPage\n\n\t// Get total count\n\tvar total int\n\terr := db.DB.QueryRow(r.Context(), countQuery, searchPattern).Scan(&total)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to get search count: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Get bookings\n\trows, err := db.DB.Query(r.Context(), searchQuery, searchPattern, perPage, offset)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to search bookings: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\n\tvar bookings []Booking\n\tbookingIDs := []string{}\n\n\tfor rows.Next() {\n\t\tvar b Booking\n\t\tvar userFullName string\n\n\t\terr := rows.Scan(&b.ID, &b.StartTime, &b.Status, &userFullName, &b.DurationMinutes, &b.AmountDue)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Failed to scan booking row: %v\", err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\t// Create minimal user with just full_name\n\t\tb.User = &UserSummary{\n\t\t\tFullName: userFullName,\n\t\t}\n\n\t\tbookings = append(bookings, b)\n\t\tbookingIDs = append(bookingIDs, b.ID)\n\t}\n\n\t// Fetch service names only (same as GetAllAdminBookingsHandler)\n\tif len(bookingIDs) > 0 {\n\t\tservicesQuery := `\n\t\t\tSELECT bs.booking_id, s.name\n\t\t\tFROM booking_services bs\n\t\t\tJOIN services s ON bs.service_id = s.id\n\t\t\tWHERE bs.booking_id = ANY($1)\n\t\t\tORDER BY bs.booking_id, s.name\n\t\t`\n\n\t\tserviceRows, err := db.DB.Query(r.Context(), servicesQuery, bookingIDs)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Failed to fetch booking services: %v\", err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tdefer serviceRows.Close()\n\n\t\tservicesByBooking := make(map[string][]BookingService)\n\n\t\tfor serviceRows.Next() {\n\t\t\tvar bookingID string\n\t\t\tvar serviceName string\n\n\t\t\tif err := serviceRows.Scan(&bookingID, &serviceName); err != nil {\n\t\t\t\tlog.Printf(\"Failed to scan service row: %v\", err)\n\t\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tservice := BookingService{\n\t\t\t\tBookingID: bookingID,\n\t\t\t\tServiceName: &serviceName,\n\t\t\t}\n\t\t\tservicesByBooking[bookingID] = append(servicesByBooking[bookingID], service)\n\t\t}\n\n\t\t// Assign services to each booking\n\t\tfor i := range bookings {\n\t\t\tif services, exists := servicesByBooking[bookings[i].ID]; exists {\n\t\t\t\tbookings[i].Services = services\n\t\t\t} else {\n\t\t\t\t// Ensure services is never nil\n\t\t\t\tbookings[i].Services = []BookingService{}\n\t\t\t}\n\t\t}\n\t}\n\n\tresponse := BookingListResponse{\n\t\tBookings: bookings,\n\t\tPage: page,\n\t\tPerPage: perPage,\n\t\tTotal: total,\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tif err := json.NewEncoder(w).Encode(response); err != nil {\n\t\tlog.Printf(\"Failed to encode response: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: CreateBookingHandler\nDoc:\nPOST /api/bookings\n\nCode:\nfunc CreateBookingHandler(w http.ResponseWriter, r *http.Request) {\n\t// Get user ID from context\n\tuserID, ok := r.Context().Value(mw.UserIDKey).(string)\n\tif !ok || userID == \"\" {\n\t\thttp.Error(w, \"Authentication required\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t// Parse and validate request\n\tvar req CreateBookingRequest\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\tlog.Printf(\"Failed to decode request: %v\", err)\n\t\thttp.Error(w, \"Invalid request\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Basic validation\n\tif req.StartTime.IsZero() {\n\t\thttp.Error(w, \"Start time is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif len(req.ServiceIDs) == 0 {\n\t\thttp.Error(w, \"At least one service is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif req.StartTime.Before(time.Now()) {\n\t\thttp.Error(w, \"Start time cannot be in the past\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Get created by from context (if available)\n\tvar createdBy *string\n\tif creatorID, ok := r.Context().Value(mw.UserIDKey).(string); ok {\n\t\tcreatedBy = &creatorID\n\t}\n\n\ttx, err := db.DB.Begin(r.Context())\n\tif err != nil {\n\t\tlog.Printf(\"Failed to start transaction: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer tx.Rollback(r.Context())\n\n\t// Insert new booking\n\tbookingQuery := `\n\t\tINSERT INTO bookings (user_id, start_time, notes, created_by)\n\t\tVALUES ($1, $2, $3, $4)\n\t\tRETURNING id, user_id, start_time, status, notes, created_at, updated_at, created_by\n\t`\n\n\tvar booking Booking\n\tbooking.User = &UserSummary{}\n\terr = tx.QueryRow(r.Context(),\n\t\tbookingQuery,\n\t\tuserID,\n\t\treq.StartTime,\n\t\treq.Notes,\n\t\tcreatedBy,\n\t).Scan(\n\t\t&booking.ID,\n\t\t&booking.User.ID,\n\t\t&booking.StartTime,\n\t\t&booking.Status,\n\t\t&booking.Notes,\n\t\t&booking.CreatedAt,\n\t\t&booking.UpdatedAt,\n\t\t&booking.CreatedBy,\n\t)\n\n\tif err != nil {\n\t\tlog.Printf(\"Failed to create booking for user %s: %v\", userID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Insert booking services\n\tserviceQuery := `\n\t\tINSERT INTO booking_services (booking_id, service_id)\n\t\tVALUES ($1, $2)\n\t`\n\tfor _, serviceID := range req.ServiceIDs {\n\t\t_, err := tx.Exec(r.Context(), serviceQuery, booking.ID, serviceID)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err := tx.Commit(r.Context()); err != nil {\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Return created booking\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusCreated)\n\tif err := json.NewEncoder(w).Encode(booking); err != nil {\n\t\tlog.Printf(\"Failed to encode booking response: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: EditBookingHandler\nDoc:\nPUT /api/bookings/{id}\n\nCode:\nfunc EditBookingHandler(w http.ResponseWriter, r *http.Request) {\n\tbookingID := chi.URLParam(r, \"id\")\n\tif bookingID == \"\" {\n\t\thttp.Error(w, \"Booking ID is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Get user ID from context\n\tuserID, ok := r.Context().Value(mw.UserIDKey).(string)\n\tif !ok || userID == \"\" {\n\t\thttp.Error(w, \"Authentication required\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t// Parse and validate request\n\tvar req EditBookingRequest\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\tlog.Printf(\"Failed to decode request: %v\", err)\n\t\thttp.Error(w, \"Invalid request\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tif req.StartTime.IsZero() {\n\t\thttp.Error(w, \"Start time is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif req.StartTime.Before(time.Now()) {\n\t\thttp.Error(w, \"Start time cannot be in the past\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Update booking start time (only for user's own bookings)\n\tquery := `\n\t\tUPDATE bookings \n\t\tSET start_time = $1, updated_at = NOW()\n\t\tWHERE id = $2 AND user_id = $3\n\t\tRETURNING id, user_id, start_time, status, notes, created_at, updated_at, created_by\n\t`\n\n\tvar booking Booking\n\tbooking.User = &UserSummary{}\n\terr := db.DB.QueryRow(r.Context(),\n\t\tquery,\n\t\treq.StartTime,\n\t\tbookingID,\n\t\tuserID,\n\t).Scan(\n\t\t&booking.ID,\n\t\t&booking.User.ID,\n\t\t&booking.StartTime,\n\t\t&booking.Status,\n\t\t&booking.Notes,\n\t\t&booking.CreatedAt,\n\t\t&booking.UpdatedAt,\n\t\t&booking.CreatedBy,\n\t)\n\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\thttp.Error(w, \"Booking not found or access denied\", http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\t\tlog.Printf(\"Failed to update booking %s for user %s: %v\", bookingID, userID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Return updated booking\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusOK)\n\tif err := json.NewEncoder(w).Encode(booking); err != nil {\n\t\tlog.Printf(\"Failed to encode booking response: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: ProgressBookingHandler\nDoc:\nPUT /api/bookings/{id}/progress\n\nCode:\nfunc ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {\n\tbookingID := chi.URLParam(r, \"id\")\n\tif bookingID == \"\" {\n\t\thttp.Error(w, \"Booking ID is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Parse and validate request\n\tvar req ProgressBookingRequest\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\tlog.Printf(\"Failed to decode request: %v\", err)\n\t\thttp.Error(w, \"Invalid request\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tallowed := map[string]bool{\n\t\t\"pending\": true, \"confirmed\": true, \"in_progress\": true,\n\t\t\"completed\": true, \"client_cancelled\": true, \"we_cancelled\": true,\n\t\t\"re-schedule\": true, \"no_show\": true,\n\t}\n\tif !allowed[req.Status] {\n\t\thttp.Error(w, \"Invalid status\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Update booking status\n\tquery := `\n\t\tUPDATE bookings \n\t\tSET status = $1, updated_at = NOW()\n\t\tWHERE id = $2\n\t\tRETURNING id, user_id, start_time, status, notes, created_at, updated_at, created_by\n\t`\n\n\tvar booking Booking\n\tbooking.User = &UserSummary{}\n\terr := db.DB.QueryRow(r.Context(),\n\t\tquery,\n\t\treq.Status,\n\t\tbookingID,\n\t).Scan(\n\t\t&booking.ID,\n\t\t&booking.User.ID,\n\t\t&booking.StartTime,\n\t\t&booking.Status,\n\t\t&booking.Notes,\n\t\t&booking.CreatedAt,\n\t\t&booking.UpdatedAt,\n\t\t&booking.CreatedBy,\n\t)\n\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\thttp.Error(w, \"Booking not found\", http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\t\tlog.Printf(\"Failed to update booking status for booking %s: %v\", bookingID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Return updated booking\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusOK)\n\tif err := json.NewEncoder(w).Encode(booking); err != nil {\n\t\tlog.Printf(\"Failed to encode booking response: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: ConfirmBookingHandler\nDoc:\nPOST /api/bookings/{id}/confirm\n\nCode:\nfunc ConfirmBookingHandler(w http.ResponseWriter, r *http.Request) {\n\tbookingID := chi.URLParam(r, \"id\")\n\tif bookingID == \"\" {\n\t\thttp.Error(w, \"Booking ID is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Parse and validate request\n\tvar req ConfirmBookingRequest\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\tlog.Printf(\"Failed to decode request: %v\", err)\n\t\thttp.Error(w, \"Invalid request\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Validate override values\n\tfor _, override := range req.ServiceOverrides {\n\t\tif override.OverridePrice != nil && *override.OverridePrice < 0 {\n\t\t\thttp.Error(w, \"Override price cannot be negative\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\tif override.OverrideDurationMinutes != nil && *override.OverrideDurationMinutes <= 0 {\n\t\t\thttp.Error(w, \"Override duration must be positive\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t}\n\n\ttx, err := db.DB.Begin(r.Context())\n\tif err != nil {\n\t\tlog.Printf(\"Failed to start transaction: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer tx.Rollback(r.Context())\n\n\t// Update booking status and notes\n\tbookingQuery := `\n\t\tUPDATE bookings \n\t\tSET status = 'confirmed', notes = COALESCE($1, notes), updated_at = NOW()\n\t\tWHERE id = $2 AND status = 'pending'\n\t\tRETURNING id, user_id, start_time, status, notes, created_at, updated_at, created_by\n\t`\n\n\tvar booking Booking\n\tbooking.User = &UserSummary{}\n\terr = tx.QueryRow(r.Context(),\n\t\tbookingQuery,\n\t\treq.Notes,\n\t\tbookingID,\n\t).Scan(\n\t\t&booking.ID,\n\t\t&booking.User.ID,\n\t\t&booking.StartTime,\n\t\t&booking.Status,\n\t\t&booking.Notes,\n\t\t&booking.CreatedAt,\n\t\t&booking.UpdatedAt,\n\t\t&booking.CreatedBy,\n\t)\n\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\thttp.Error(w, \"Booking not found or already confirmed\", http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\t\tlog.Printf(\"Failed to confirm booking %s: %v\", bookingID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Update service overrides individually\n\tif len(req.ServiceOverrides) > 0 {\n\t\t// First, verify all service IDs belong to this booking\n\t\tserviceCheckQuery := `\n\t\t\tSELECT COUNT(*) FROM booking_services \n\t\t\tWHERE booking_id = $1 AND service_id = ANY($2)\n\t\t`\n\t\tserviceIDs := make([]string, len(req.ServiceOverrides))\n\t\tfor i, override := range req.ServiceOverrides {\n\t\t\tserviceIDs[i] = override.ServiceID\n\t\t}\n\n\t\tvar count int\n\t\terr = tx.QueryRow(r.Context(), serviceCheckQuery, bookingID, serviceIDs).Scan(&count)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Failed to verify services for booking %s: %v\", bookingID, err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tif count != len(req.ServiceOverrides) {\n\t\t\thttp.Error(w, \"One or more service IDs do not belong to this booking\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\t// Update each service override\n\t\tserviceUpdateQuery := `\n\t\t\tUPDATE booking_services \n\t\t\tSET override_price = $1,\n\t\t\t override_duration_minutes = $2\n\t\t\tWHERE booking_id = $3 AND service_id = $4\n\t\t`\n\n\t\tfor _, override := range req.ServiceOverrides {\n\t\t\t_, err := tx.Exec(r.Context(),\n\t\t\t\tserviceUpdateQuery,\n\t\t\t\toverride.OverridePrice,\n\t\t\t\toverride.OverrideDurationMinutes,\n\t\t\t\tbookingID,\n\t\t\t\toverride.ServiceID,\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"Failed to update service override for booking %s, service %s: %v\",\n\t\t\t\t\tbookingID, override.ServiceID, err)\n\t\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := tx.Commit(r.Context()); err != nil {\n\t\tlog.Printf(\"Failed to commit booking confirmation: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Return confirmed booking\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusOK)\n\tif err := json.NewEncoder(w).Encode(booking); err != nil {\n\t\tlog.Printf(\"Failed to encode booking response: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: DeleteBookingHandler\nDoc:\nDELETE /api/bookings/{id}\n\nCode:\nfunc DeleteBookingHandler(w http.ResponseWriter, r *http.Request) {\n\tbookingID := chi.URLParam(r, \"id\")\n\tif bookingID == \"\" {\n\t\thttp.Error(w, \"Booking ID is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Get user ID from context\n\tuserID, ok := r.Context().Value(mw.UserIDKey).(string)\n\tif !ok || userID == \"\" {\n\t\thttp.Error(w, \"Authentication required\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t// Check if booking has payments\n\tvar paymentCount int\n\tpaymentCheckQuery := \"SELECT COUNT(*) FROM payments WHERE booking_id = $1\"\n\terr := db.DB.QueryRow(r.Context(), paymentCheckQuery, bookingID).Scan(&paymentCount)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to check booking %s for user %s: %v\", bookingID, userID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif paymentCount > 0 {\n\t\t// Parse delete reason for bookings with payments\n\t\tvar req DeleteBookingRequest\n\t\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\t\tlog.Printf(\"Failed to decode request: %v\", err)\n\t\t\thttp.Error(w, \"Invalid request\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\tallowed := map[string]bool{\n\t\t\t\"client_cancelled\": true, \"we_cancelled\": true, \"re-schedule\": true, \"no_show\": true,\n\t\t}\n\t\tif !allowed[req.Reason] {\n\t\t\thttp.Error(w, \"Invalid reason\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\t// Update booking status instead of deleting\n\t\tquery := `\n\t\t\tUPDATE bookings \n\t\t\tSET status = $1, updated_at = NOW()\n\t\t\tWHERE id = $2 AND user_id = $3\n\t\t`\n\t\tresult, err := db.DB.Exec(r.Context(), query, req.Reason, bookingID, userID)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Failed to cancel booking %s for user %s: %v\", bookingID, userID, err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tif result.RowsAffected() == 0 {\n\t\t\thttp.Error(w, \"Booking not found or access denied\", http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\tw.WriteHeader(http.StatusOK)\n\t\tjson.NewEncoder(w).Encode(map[string]interface{}{\n\t\t\t\"message\": \"Booking cancelled successfully\",\n\t\t\t\"id\": bookingID,\n\t\t\t\"status\": req.Reason,\n\t\t})\n\t\treturn\n\t}\n\n\t// Hard delete if no payments exist\n\tquery := \"DELETE FROM bookings WHERE id = $1 AND user_id = $2\"\n\tresult, err := db.DB.Exec(r.Context(), query, bookingID, userID)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to delete booking %s for user %s: %v\", bookingID, userID, err)\n\t\thttp.Error(w, \"Failed to delete booking\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif result.RowsAffected() == 0 {\n\t\thttp.Error(w, \"Booking not found or access denied\", http.StatusNotFound)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusOK)\n\tjson.NewEncoder(w).Encode(map[string]interface{}{\n\t\t\"message\": \"Booking deleted successfully\",\n\t\t\"id\": bookingID,\n\t})\n}\n", + "File: /home/popertots/Crussell/backend/handlers/bookings/bookings.go\nType: func\nName: GetBookingHandler\nDoc:\nGET /api/bookings/{id}\n\nCode:\nfunc GetBookingHandler(w http.ResponseWriter, r *http.Request) {\n\tbookingID := chi.URLParam(r, \"id\")\n\tif bookingID == \"\" {\n\t\thttp.Error(w, \"Booking ID is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tuserID, ok := r.Context().Value(mw.UserIDKey).(string)\n\tif !ok || userID == \"\" {\n\t\thttp.Error(w, \"Authentication required\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t// ----------------------------\n\t// 1. Fetch booking\n\t// ----------------------------\n\tvar booking Booking\n\tbooking.Payments = []Payment{}\n\tbooking.User = &UserSummary{}\n\tvar createdBy sql.NullString\n\terr := db.DB.QueryRow(r.Context(), `\n\t\tSELECT id, user_id, start_time, status, notes, created_at, updated_at, created_by\n\t\tFROM bookings\n\t\tWHERE id = $1 AND user_id = $2\n\t`, bookingID, userID).Scan(\n\t\t&booking.ID, &booking.User.ID, &booking.StartTime, &booking.Status,\n\t\t&booking.Notes, &booking.CreatedAt, &booking.UpdatedAt, &createdBy,\n\t)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\thttp.Error(w, \"Booking not found or access denied\", http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\t\tlog.Printf(\"Failed to fetch booking %s for user %s: %v\", bookingID, userID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tif createdBy.Valid {\n\t\tbooking.CreatedBy = &createdBy.String\n\t}\n\n\t// ----------------------------\n\t// 2. Fetch services\n\t// ----------------------------\n\tvar totalAmount float64\n\tvar durationMinutes int\n\n\tserviceRows, err := db.DB.Query(r.Context(), `\n SELECT \n bs.service_id, bs.override_price, bs.override_duration_minutes,\n s.name, s.description, s.price, s.duration_minutes\n FROM booking_services bs\n LEFT JOIN services s ON bs.service_id = s.id\n WHERE bs.booking_id = $1\n ORDER BY s.name\n `, bookingID)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to fetch services for booking %s: %v\", bookingID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer serviceRows.Close()\n\n\tfor serviceRows.Next() {\n\t\tvar s BookingService\n\t\tvar overridePrice sql.NullFloat64\n\t\tvar overrideDuration sql.NullInt32\n\t\tvar name, description sql.NullString\n\t\tvar basePrice sql.NullFloat64\n\t\tvar baseDuration sql.NullInt32\n\n\t\terr := serviceRows.Scan(\n\t\t\t&s.ServiceID,\n\t\t\t&overridePrice, &overrideDuration,\n\t\t\t&name, &description, &basePrice, &baseDuration,\n\t\t)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Failed to scan service row for booking %s: %v\", bookingID, err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tif overridePrice.Valid {\n\t\t\ts.OverridePrice = &overridePrice.Float64\n\t\t\ttotalAmount += *s.OverridePrice\n\t\t} else if basePrice.Valid {\n\t\t\ttotalAmount += basePrice.Float64\n\t\t}\n\n\t\tif overrideDuration.Valid {\n\t\t\td := int(overrideDuration.Int32)\n\t\t\ts.OverrideDurationMinutes = &d\n\t\t\tdurationMinutes += *s.OverrideDurationMinutes\n\t\t} else if baseDuration.Valid {\n\t\t\tdurationMinutes += int(baseDuration.Int32)\n\t\t}\n\n\t\tif name.Valid {\n\t\t\ts.ServiceName = &name.String\n\t\t}\n\t\tif description.Valid {\n\t\t\ts.ServiceDescription = &description.String\n\t\t}\n\t\tif basePrice.Valid {\n\t\t\ts.Price = &basePrice.Float64\n\t\t}\n\t\tif baseDuration.Valid {\n\t\t\td := int(baseDuration.Int32)\n\t\t\ts.DurationMinutes = &d\n\t\t}\n\n\t\tbooking.Services = append(booking.Services, s)\n\t}\n\n\t// ----------------------------\n\t// 3. Fetch payments\n\t// ----------------------------\n\tvar amountPaid float64\n\tpaymentRows, err := db.DB.Query(r.Context(), `\n SELECT\n id, payment_type, payment_method, vendor_code, invoice_number,\n status, amount, is_vat_applicable, vat_rate, vat_amount, net_amount,\n created_at, updated_at, created_by\n FROM payments\n WHERE booking_id = $1\n ORDER BY created_at ASC\n `, bookingID)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to fetch payments for booking %s: %v\", bookingID, err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer paymentRows.Close()\n\n\tfor paymentRows.Next() {\n\t\tvar p Payment\n\t\tvar vendorCode sql.NullString\n\t\tvar invoiceNumber sql.NullInt32\n\t\tvar vatRate, vatAmount, netAmount sql.NullFloat64\n\t\tvar createdBy sql.NullString\n\n\t\terr := paymentRows.Scan(\n\t\t\t&p.ID, &p.PaymentType, &p.PaymentMethod, &vendorCode, &invoiceNumber,\n\t\t\t&p.Status, &p.Amount, &p.IsVATApplicable, &vatRate, &vatAmount, &netAmount,\n\t\t\t&p.CreatedAt, &p.UpdatedAt, &createdBy,\n\t\t)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Failed to scan payment row for booking %s: %v\", bookingID, err)\n\t\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tif vendorCode.Valid {\n\t\t\tp.VendorCode = &vendorCode.String\n\t\t}\n\t\tif invoiceNumber.Valid {\n\t\t\tnum := int(invoiceNumber.Int32)\n\t\t\tp.InvoiceNumber = &num\n\t\t}\n\t\tif vatRate.Valid {\n\t\t\tp.VATRate = &vatRate.Float64\n\t\t}\n\t\tif vatAmount.Valid {\n\t\t\tp.VATAmount = &vatAmount.Float64\n\t\t}\n\t\tif netAmount.Valid {\n\t\t\tp.NetAmount = &netAmount.Float64\n\t\t}\n\t\tif createdBy.Valid {\n\t\t\tp.CreatedBy = &createdBy.String\n\t\t}\n\n\t\tif p.Status == \"completed\" {\n\t\t\tamountPaid += p.Amount\n\t\t}\n\n\t\tbooking.Payments = append(booking.Payments, p)\n\t}\n\n\tamountDue := totalAmount - amountPaid\n\n\t// Create enhanced response with user-friendly totals\n\tenhancedResponse := struct {\n\t\tBooking Booking `json:\"booking\"`\n\t\tTotalAmount float64 `json:\"total_amount\"`\n\t\tAmountPaid float64 `json:\"amount_paid\"`\n\t\tAmountDue float64 `json:\"amount_due\"`\n\t\tDurationMinutes int `json:\"duration_minutes\"`\n\t}{\n\t\tBooking: booking,\n\t\tTotalAmount: totalAmount,\n\t\tAmountPaid: amountPaid,\n\t\tAmountDue: amountDue,\n\t\tDurationMinutes: durationMinutes,\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tif err := json.NewEncoder(w).Encode(enhancedResponse); err != nil {\n\t\tlog.Printf(\"Failed to encode booking response: %v\", err)\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/handlers/auth/social.go\nLanguage: go\n\npackage auth", + "File: /home/popertots/Crussell/backend/handlers/auth/local.go\nType: var\nName: titleCaser\nCode:\n\ttitleCaser = cases.Title(language.English)\n", + "File: /home/popertots/Crussell/backend/handlers/auth/local.go\nType: var\nName: loginStateMu\nDoc:\nLogin state management\n\nCode:\n\tloginStateMu sync.Mutex\n", + "File: /home/popertots/Crussell/backend/handlers/auth/local.go\nType: var\nName: loginInProgress\nDoc:\nLogin state management\n\nCode:\n\tloginInProgress = make(map[string]bool)\n", + "File: /home/popertots/Crussell/backend/handlers/auth/local.go\nType: var\nName: loginAttempts\nDoc:\nLogin state management\n\nCode:\n\tloginAttempts = make(map[string]time.Time)\n", + "File: /home/popertots/Crussell/backend/handlers/auth/local.go\nType: func\nName: init\nCode:\nfunc init() {\n\tgo func() {\n\t\tticker := time.NewTicker(1 * time.Hour)\n\t\tdefer ticker.Stop()\n\n\t\tfor range ticker.C {\n\t\t\tloginStateMu.Lock()\n\t\t\tnow := time.Now()\n\t\t\tfor userID, lastAttempt := range loginAttempts {\n\t\t\t\t// Remove attempts older than 1 hour\n\t\t\t\tif now.Sub(lastAttempt) > 1*time.Hour {\n\t\t\t\t\tdelete(loginAttempts, userID)\n\t\t\t\t}\n\t\t\t}\n\t\t\tloginStateMu.Unlock()\n\t\t}\n\t}()\n}\n", + "File: /home/popertots/Crussell/backend/handlers/auth/local.go\nType: struct\nName: RegisterRequest\nCode:\ntype RegisterRequest struct {\n\tFirstName string `json:\"firstName\"`\n\tLastName string `json:\"lastName\"`\n\tEmail string `json:\"email\"`\n\tPassword string `json:\"password\"`\n\tPhone string `json:\"phone\"`\n\tDateOfBirth string `json:\"dateOfBirth\"`\n\tAgreedToPolicy bool `json:\"agreedToPolicy\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/auth/local.go\nType: struct\nName: LoginRequest\nCode:\ntype LoginRequest struct {\n\tEmail string `json:\"email\"`\n\tPassword string `json:\"password\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/auth/local.go\nType: func\nName: RegisterHandler\nDoc:\nPOST /api/register\n\nCode:\nfunc RegisterHandler(w http.ResponseWriter, r *http.Request) {\n\tvar req RegisterRequest\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\thttp.Error(w, \"invalid request\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Must accept terms\n\tif !req.AgreedToPolicy {\n\t\thttp.Error(w, \"must agree to terms\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Normalize input\n\treq.Email = strings.ToLower(strings.TrimSpace(req.Email))\n\treq.FirstName = strings.TrimSpace(req.FirstName)\n\treq.LastName = strings.TrimSpace(req.LastName)\n\treq.Phone = strings.TrimSpace(req.Phone)\n\treq.DateOfBirth = strings.TrimSpace(req.DateOfBirth)\n\n\t// Check required fields\n\tif req.FirstName == \"\" || req.LastName == \"\" || req.Email == \"\" || req.Phone == \"\" || req.DateOfBirth == \"\" {\n\t\thttp.Error(w, \"all fields are required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Validate name (unicode letters, spaces, hyphen, apostrophe, dot)\n\tnameRegex := regexp.MustCompile(`^[\\p{L}\\p{M}\\s\\-'\\.]+$`)\n\n\tif !nameRegex.MatchString(req.FirstName) {\n\t\thttp.Error(w, \"invalid characters in name\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Validate length\n\tif len(req.FirstName) > 50 || len(req.FirstName) < 1 {\n\t\thttp.Error(w, \"first name must be 1-50 characters\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif len(req.LastName) > 50 || len(req.LastName) < 1 {\n\t\thttp.Error(w, \"last name must be 1-50 characters\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Validate email format\n\t_, err := mail.ParseAddress(req.Email)\n\tif err != nil {\n\t\thttp.Error(w, \"invalid email format\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Normalize phone (remove spaces, hyphens, parentheses)\n\treq.Phone = strings.Map(func(r rune) rune {\n\t\tif r >= '0' && r <= '9' || r == '+' {\n\t\t\treturn r\n\t\t}\n\t\treturn -1\n\t}, req.Phone)\n\n\t// Validate UK phone number\n\tphone, err := ValidateUKPhoneNumber(req.Phone)\n\tif err != nil {\n\t\thttp.Error(w, \"invalid phone number format\", http.StatusBadRequest)\n\t\treturn\n\t}\n\treq.Phone = strings.TrimSpace(phone)\n\n\t// Convert names to title case\n\treq.FirstName = titleCaser.String(strings.ToLower(req.FirstName))\n\treq.LastName = titleCaser.String(strings.ToLower(req.LastName))\n\n\t// Parse date of birth\n\tdob, err := time.Parse(\"2006-01-02\", req.DateOfBirth)\n\tif err != nil {\n\t\thttp.Error(w, \"invalid date format\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Reject if younger than 16\n\tif !dob.Before(time.Now().AddDate(-16, 0, 0)) {\n\t\thttp.Error(w, \"account creation prohibited for users under 16. Please call to book an appointment.\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Hash password\n\thash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)\n\tif err != nil {\n\t\thttp.Error(w, \"server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\ttx, err := db.DB.Begin(r.Context())\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\thttp.Error(w, \"server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer tx.Rollback(r.Context())\n\n\tnow := time.Now()\n\n\t// Insert and return the generated ID\n\tvar userID string\n\terr = tx.QueryRow(r.Context(), `\n\t\tINSERT INTO users\n\t\t\t(n_first_name, n_last_name, phone, date_of_birth, email, password_hash, \n\t\t\t account_type, privacy_policy_and_terms_consent, policy_consent_updated_at, \n\t\t\t created_at, updated_at)\n\t\tVALUES\n\t\t\t($1, $2, $3, $4, $5, $6, 'email', $7, $8, $8, $8)\n\t\tRETURNING id\n\t`, req.FirstName, req.LastName, req.Phone, dob, req.Email, string(hash), req.AgreedToPolicy, now).Scan(&userID)\n\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\tif strings.Contains(err.Error(), \"duplicate key\") {\n\t\t\thttp.Error(w, \"an account with this email already exists\", http.StatusConflict)\n\t\t} else {\n\t\t\thttp.Error(w, \"could not create user\", http.StatusInternalServerError)\n\t\t}\n\t\treturn\n\t}\n\n\tif err := tx.Commit(r.Context()); err != nil {\n\t\thttp.Error(w, \"server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tgo func() {\n\t\tinput := dav.ContactInput{\n\t\t\tUserID: userID,\n\t\t\tFirstName: req.FirstName,\n\t\t\tLastName: req.LastName,\n\t\t\tEmail: req.Email,\n\t\t\tPhone: req.Phone,\n\t\t\tDOB: req.DateOfBirth,\n\t\t}\n\t\tif err := dav.Service.CreateContact(1, userID, input); err != nil {\n\t\t\tlog.Printf(\"Warning: Failed to create contact in DAV for user %s: %v\", userID, err)\n\t\t}\n\t}()\n\n\tw.WriteHeader(http.StatusCreated)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/auth/local.go\nType: func\nName: ValidateUKPhoneNumber\nCode:\nfunc ValidateUKPhoneNumber(phone string) (string, error) {\n\tnum, err := phonenumbers.Parse(phone, \"GB\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif !phonenumbers.IsValidNumber(num) {\n\t\treturn \"\", fmt.Errorf(\"invalid phone number\")\n\t}\n\n\t// Check if it's actually a UK number\n\tif phonenumbers.GetRegionCodeForNumber(num) != \"GB\" {\n\t\treturn \"\", fmt.Errorf(\"only UK numbers allowed\")\n\t}\n\n\t// Format in E.164 format (+44...)\n\treturn phonenumbers.Format(num, phonenumbers.E164), nil\n}\n", + "File: /home/popertots/Crussell/backend/handlers/auth/local.go\nType: func\nName: LoginHandler\nDoc:\nPOST /api/login\n\nCode:\nfunc LoginHandler(w http.ResponseWriter, r *http.Request) {\n\tvar req LoginRequest\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\thttp.Error(w, \"invalid request\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Normalize email\n\treq.Email = strings.ToLower(strings.TrimSpace(req.Email))\n\n\tvar userID, passwordHash, role string\n\tctx := context.Background()\n\terr := db.DB.QueryRow(ctx, `\n\t\tSELECT id, password_hash, account_role \n\t\tFROM users\n\t\tWHERE email = $1 AND account_type = 'email'\n\t`, req.Email).Scan(&userID, &passwordHash, &role)\n\n\tif err != nil {\n\t\thttp.Error(w, \"invalid credentials\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t// Check if user is already logging in\n\tloginStateMu.Lock()\n\tif loginInProgress[userID] {\n\t\tloginStateMu.Unlock()\n\t\thttp.Error(w, \"login already in progress\", http.StatusConflict) // 409\n\t\treturn\n\t}\n\tloginInProgress[userID] = true\n\tloginStateMu.Unlock()\n\n\t// Always clear flag when done\n\tdefer func() {\n\t\tloginStateMu.Lock()\n\t\tdelete(loginInProgress, userID)\n\t\tloginStateMu.Unlock()\n\t}()\n\n\t// Enforce 1 attempt per 5s\n\tloginStateMu.Lock()\n\tif last, ok := loginAttempts[userID]; ok {\n\t\tsince := time.Since(last)\n\t\tif since < 5*time.Second {\n\t\t\twait := 5*time.Second - since\n\t\t\tloginStateMu.Unlock()\n\t\t\ttime.Sleep(wait)\n\t\t} else {\n\t\t\tloginStateMu.Unlock()\n\t\t}\n\t} else {\n\t\tloginStateMu.Unlock()\n\t}\n\n\t// Verify password\n\tif err := bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(req.Password)); err != nil {\n\t\tloginStateMu.Lock()\n\t\tloginAttempts[userID] = time.Now()\n\t\tloginStateMu.Unlock()\n\n\t\thttp.Error(w, \"invalid credentials\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t// On success, clear attempts\n\tloginStateMu.Lock()\n\tdelete(loginAttempts, userID)\n\tloginStateMu.Unlock()\n\n\t// Update last login\n\t_, err = db.DB.Exec(ctx, `UPDATE users SET last_login_at = NOW() WHERE id = $1`, userID)\n\tif err != nil {\n\t\tfmt.Println(\"Failed to update last_login_at:\", err)\n\t}\n\n\t// Generate JWT\n\ttokenString, err := auth.GenerateToken(userID, role)\n\tif err != nil {\n\t\thttp.Error(w, \"could not generate token\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(auth.AuthResponse{Token: tokenString})\n}\n", + "File: /home/popertots/Crussell/backend/handlers/auth/local.go\nType: func\nName: RefreshTokenHandler\nDoc:\nPOST /api/refresh-token (requires auth middleware)\n\nCode:\nfunc RefreshTokenHandler(w http.ResponseWriter, r *http.Request) {\n\tuserID, _ := mw.GetUserID(r.Context())\n\trole, _ := mw.GetUserRole(r.Context())\n\n\t// Verify user still exists and role hasn't changed\n\tvar currentRole string\n\terr := db.DB.QueryRow(r.Context(), `\n\t\tSELECT account_role FROM users WHERE id = $1\n\t`, userID).Scan(¤tRole)\n\n\tif err != nil {\n\t\thttp.Error(w, \"user not found\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t// If role changed, force re-login\n\tif currentRole != role {\n\t\thttp.Error(w, \"role changed, please log in again\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t// Generate new token\n\tnewToken, err := auth.GenerateToken(userID, currentRole)\n\tif err != nil {\n\t\thttp.Error(w, \"could not generate token\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(auth.AuthResponse{Token: newToken})\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/exceptional-hours.go\nType: struct\nName: ExceptionalHours\nCode:\ntype ExceptionalHours struct {\n\tID int `json:\"id\"`\n\tGroupID int `json:\"groupId\"`\n\tWeekday int `json:\"weekday\"`\n\tStartTime string `json:\"startTime\"`\n\tEndTime string `json:\"endTime\"`\n\tIsOpen bool `json:\"isOpen\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/exceptional-hours.go\nType: struct\nName: ExceptionalGroup\nCode:\ntype ExceptionalGroup struct {\n\tID int `json:\"id\"`\n\tName string `json:\"name\"`\n\tDescription string `json:\"description\"`\n\tHours []ExceptionalHours `json:\"hours,omitempty\"`\n\tWeekStarts []string `json:\"weekStarts,omitempty\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/exceptional-hours.go\nType: func\nName: ListExceptionalGroups\nDoc:\n--- List Groups with Hours and Applications ---\n\nCode:\nfunc ListExceptionalGroups(w http.ResponseWriter, r *http.Request) {\n\trows, err := db.DB.Query(r.Context(), `\n\t\tSELECT id, name, description\n\t\tFROM exceptional_working_hours_groups\n\t\tORDER BY id DESC\n\t`)\n\tif err != nil {\n\t\thttp.Error(w, \"failed to fetch groups\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\n\tvar groups []ExceptionalGroup\n\tfor rows.Next() {\n\t\tvar g ExceptionalGroup\n\t\tif err := rows.Scan(&g.ID, &g.Name, &g.Description); err != nil {\n\t\t\thttp.Error(w, \"failed to scan group\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\t// Load 7-day hours\n\t\thoursRows, err := db.DB.Query(r.Context(), `\n\t\t\tSELECT id, weekday, start_time::text, end_time::text, is_open\n\t\t\tFROM exceptional_working_hours\n\t\t\tWHERE group_id=$1 ORDER BY weekday\n\t\t`, g.ID)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"failed to fetch group hours\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tfor hoursRows.Next() {\n\t\t\tvar h ExceptionalHours\n\t\t\tif err := hoursRows.Scan(&h.ID, &h.Weekday, &h.StartTime, &h.EndTime, &h.IsOpen); err != nil {\n\t\t\t\thoursRows.Close()\n\t\t\t\thttp.Error(w, \"failed to scan hours\", http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\t\t\th.GroupID = g.ID\n\t\t\tg.Hours = append(g.Hours, h)\n\t\t}\n\t\thoursRows.Close()\n\n\t\tif err := hoursRows.Err(); err != nil {\n\t\t\thttp.Error(w, \"error iterating hours\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\t// Load applied week starts\n\t\tweekRows, err := db.DB.Query(r.Context(), `\n\t\t\tSELECT week_start\n\t\t\tFROM exceptional_group_applications\n\t\t\tWHERE group_id=$1 ORDER BY week_start\n\t\t`, g.ID)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"failed to fetch applications\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tfor weekRows.Next() {\n\t\t\tvar weekStart time.Time\n\t\t\tif err := weekRows.Scan(&weekStart); err != nil {\n\t\t\t\tweekRows.Close()\n\t\t\t\thttp.Error(w, \"failed to scan week_start\", http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tg.WeekStarts = append(g.WeekStarts, weekStart.Format(\"2006-01-02\"))\n\t\t}\n\t\tweekRows.Close()\n\n\t\tif err := weekRows.Err(); err != nil {\n\t\t\thttp.Error(w, \"error iterating applications\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tgroups = append(groups, g)\n\t}\n\n\tif err := rows.Err(); err != nil {\n\t\thttp.Error(w, \"error iterating groups\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(groups)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/exceptional-hours.go\nType: func\nName: CreateExceptionalGroup\nDoc:\n--- Create Group with Hours and Applications (bulk) ---\n\nCode:\nfunc CreateExceptionalGroup(w http.ResponseWriter, r *http.Request) {\n\tvar g ExceptionalGroup\n\tif err := json.NewDecoder(r.Body).Decode(&g); err != nil {\n\t\thttp.Error(w, \"invalid payload\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tif len(g.Hours) != 7 {\n\t\thttp.Error(w, \"must provide exactly 7 weekday entries (0-6)\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Validate weekdays and parse week_starts\n\tweekdaysSeen := make(map[int]bool)\n\tfor _, h := range g.Hours {\n\t\tif h.Weekday < 0 || h.Weekday > 6 {\n\t\t\thttp.Error(w, \"weekday must be 0-6\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\tif weekdaysSeen[h.Weekday] {\n\t\t\thttp.Error(w, \"duplicate weekday entries\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\tweekdaysSeen[h.Weekday] = true\n\t}\n\n\tvar parsedWeeks []time.Time\n\tukLocation, _ := time.LoadLocation(\"Europe/London\")\n\tfor _, ws := range g.WeekStarts {\n\t\tweekStart, err := time.ParseInLocation(\"2006-01-02\", ws, ukLocation)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"invalid week_start format, expected YYYY-MM-DD\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\tif weekStart.Weekday() != time.Monday {\n\t\t\thttp.Error(w, \"week_start must be a Monday\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\t// Normalize to UK midnight\n\t\tweekStart = time.Date(weekStart.Year(), weekStart.Month(), weekStart.Day(), 0, 0, 0, 0, ukLocation)\n\t\tparsedWeeks = append(parsedWeeks, weekStart)\n\t}\n\n\ttx, err := db.DB.Begin(r.Context())\n\tif err != nil {\n\t\thttp.Error(w, \"failed to start transaction\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer tx.Rollback(r.Context())\n\n\t// Create group\n\terr = tx.QueryRow(r.Context(), `\n\t\tINSERT INTO exceptional_working_hours_groups (name, description)\n\t\tVALUES ($1, $2) RETURNING id\n\t`, g.Name, g.Description).Scan(&g.ID)\n\tif err != nil {\n\t\thttp.Error(w, \"failed to create group\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Insert hours and collect their IDs\n\tinputHours := g.Hours\n\tg.Hours = []ExceptionalHours{} // Clear and rebuild with IDs\n\tfor _, h := range inputHours {\n\t\tvar id int\n\t\terr := tx.QueryRow(r.Context(), `\n\t\t\tINSERT INTO exceptional_working_hours (group_id, weekday, start_time, end_time, is_open)\n\t\t\tVALUES ($1, $2, $3, $4, $5)\n\t\t\tRETURNING id\n\t\t`, g.ID, h.Weekday, h.StartTime, h.EndTime, h.IsOpen).Scan(&id)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"failed to insert hours\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\th.ID = id\n\t\th.GroupID = g.ID\n\t\tg.Hours = append(g.Hours, h)\n\t}\n\n\t// Insert applications\n\tfor _, weekStart := range parsedWeeks {\n\t\t_, err := tx.Exec(r.Context(), `\n\t\t\tINSERT INTO exceptional_group_applications (group_id, week_start)\n\t\t\tVALUES ($1, $2)\n\t\t`, g.ID, weekStart)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"failed to insert application\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err := tx.Commit(r.Context()); err != nil {\n\t\thttp.Error(w, \"failed to commit transaction\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusCreated)\n\tjson.NewEncoder(w).Encode(g)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/exceptional-hours.go\nType: func\nName: DeleteExceptionalGroup\nDoc:\n--- Delete Group (cascades to hours and applications) ---\n\nCode:\nfunc DeleteExceptionalGroup(w http.ResponseWriter, r *http.Request) {\n\t// Extract group ID from URL path or query params\n\t// Assuming you have a router that provides this, e.g. chi, mux, etc.\n\t// For this example, we'll use query param: DELETE /exceptional-groups?id=123\n\tidStr := r.URL.Query().Get(\"id\")\n\tif idStr == \"\" {\n\t\thttp.Error(w, \"missing id parameter\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tid, err := strconv.Atoi(idStr)\n\tif err != nil {\n\t\thttp.Error(w, \"invalid id parameter\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tresult, err := db.DB.Exec(r.Context(), `\n\t\tDELETE FROM exceptional_working_hours_groups WHERE id=$1\n\t`, id)\n\tif err != nil {\n\t\thttp.Error(w, \"failed to delete group\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\trowsAffected := result.RowsAffected()\n\tif rowsAffected == 0 {\n\t\thttp.Error(w, \"group not found\", http.StatusNotFound)\n\t\treturn\n\t}\n\n\tw.WriteHeader(http.StatusNoContent)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/exceptional-hours.go\nType: func\nName: UpdateExceptionalApplications\nDoc:\n--- Update Applied Weeks (replaces all applications for a group) ---\n\nCode:\nfunc UpdateExceptionalApplications(w http.ResponseWriter, r *http.Request) {\n\tvar req struct {\n\t\tGroupID int `json:\"groupId\"`\n\t\tWeekStarts []string `json:\"weekStarts\"`\n\t}\n\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\thttp.Error(w, \"invalid payload\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Validate and parse weeks\n\tvar parsedWeeks []time.Time\n\tukLocation, _ := time.LoadLocation(\"Europe/London\")\n\tfor _, ws := range req.WeekStarts {\n\t\tweekStart, err := time.ParseInLocation(\"2006-01-02\", ws, ukLocation)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"invalid week_start format, expected YYYY-MM-DD\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\tif weekStart.Weekday() != time.Monday {\n\t\t\thttp.Error(w, \"week_start must be a Monday\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\t// Normalize to UK midnight\n\t\tweekStart = time.Date(weekStart.Year(), weekStart.Month(), weekStart.Day(), 0, 0, 0, 0, ukLocation)\n\t\tparsedWeeks = append(parsedWeeks, weekStart)\n\t}\n\n\ttx, err := db.DB.Begin(r.Context())\n\tif err != nil {\n\t\thttp.Error(w, \"failed to start transaction\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer tx.Rollback(r.Context())\n\n\t// Delete existing applications for this group\n\t_, err = tx.Exec(r.Context(), `\n\t\tDELETE FROM exceptional_group_applications WHERE group_id=$1\n\t`, req.GroupID)\n\tif err != nil {\n\t\thttp.Error(w, \"failed to delete existing applications\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Insert new applications\n\tfor _, weekStart := range parsedWeeks {\n\t\t_, err := tx.Exec(r.Context(), `\n\t\t\tINSERT INTO exceptional_group_applications (group_id, week_start)\n\t\t\tVALUES ($1, $2)\n\t\t`, req.GroupID, weekStart)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"failed to insert application\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err := tx.Commit(r.Context()); err != nil {\n\t\thttp.Error(w, \"failed to commit transaction\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.WriteHeader(http.StatusNoContent)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/default-hours.go\nType: struct\nName: DefaultHours\nDoc:\n--- Types ---\n\nCode:\ntype DefaultHours struct {\n\tWeekday int `json:\"weekday\"`\n\tStartTime string `json:\"startTime\"`\n\tEndTime string `json:\"endTime\"`\n\tIsOpen bool `json:\"isOpen\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/default-hours.go\nType: struct\nName: DayWorkingHours\nCode:\ntype DayWorkingHours struct {\n\tDate string `json:\"date\"`\n\tWeekday int `json:\"weekday\"`\n\tStartTime string `json:\"startTime\"`\n\tEndTime string `json:\"endTime\"`\n\tIsOpen bool `json:\"isOpen\"`\n\tSource string `json:\"source\"` // \"default\" or \"exceptional\"\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/default-hours.go\nType: func\nName: GetDefaultHours\nDoc:\n--- Default Hours Handlers ---\n\nCode:\nfunc GetDefaultHours(w http.ResponseWriter, r *http.Request) {\n\trows, err := db.DB.Query(r.Context(), `\n\t\tSELECT weekday, start_time::text, end_time::text, is_open\n\t\tFROM working_hours ORDER BY weekday\n\t`)\n\tif err != nil {\n\t\thttp.Error(w, \"failed to fetch default hours\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\n\tvar hours []DefaultHours\n\tfor rows.Next() {\n\t\tvar h DefaultHours\n\t\tif err := rows.Scan(&h.Weekday, &h.StartTime, &h.EndTime, &h.IsOpen); err != nil {\n\t\t\thttp.Error(w, \"failed to scan default hours\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\thours = append(hours, h)\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(hours)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/default-hours.go\nType: func\nName: UpdateDefaultHours\nCode:\nfunc UpdateDefaultHours(w http.ResponseWriter, r *http.Request) {\n\tvar hours []DefaultHours\n\tif err := json.NewDecoder(r.Body).Decode(&hours); err != nil {\n\t\thttp.Error(w, \"invalid payload\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\ttx, err := db.DB.Begin(r.Context())\n\tif err != nil {\n\t\thttp.Error(w, \"failed to start tx\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer tx.Rollback(r.Context())\n\n\tfor _, h := range hours {\n\t\t_, err := tx.Exec(r.Context(), `\n\t\t\tUPDATE working_hours\n\t\t\tSET start_time=$1,end_time=$2,is_open=$3\n\t\t\tWHERE weekday=$4\n\t\t`, h.StartTime, h.EndTime, h.IsOpen, h.Weekday)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"failed to update default hours\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err := tx.Commit(r.Context()); err != nil {\n\t\thttp.Error(w, \"failed to commit\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.WriteHeader(http.StatusNoContent)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/default-hours.go\nType: func\nName: GetWorkingHours\nDoc:\n--- GetWorkingHours (merged default + applied exceptions, UK-local) ---\n\nCode:\nfunc GetWorkingHours(w http.ResponseWriter, r *http.Request) {\n\tstartStr := r.URL.Query().Get(\"start\")\n\tendStr := r.URL.Query().Get(\"end\")\n\tif startStr == \"\" || endStr == \"\" {\n\t\thttp.Error(w, \"start and end query params required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tukLocation, _ := time.LoadLocation(\"Europe/London\")\n\tstart, err := time.ParseInLocation(\"2006-01-02\", startStr, ukLocation)\n\tif err != nil {\n\t\thttp.Error(w, \"invalid start date\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tend, err := time.ParseInLocation(\"2006-01-02\", endStr, ukLocation)\n\tif err != nil {\n\t\thttp.Error(w, \"invalid end date\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Set to local start/end of day\n\tstart = time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, ukLocation)\n\tend = time.Date(end.Year(), end.Month(), end.Day(), 23, 59, 59, 999999999, ukLocation)\n\n\t// Load default hours\n\tdefaultMap := map[int]DefaultHours{}\n\tdefRows, _ := db.DB.Query(r.Context(), `\n\t\tSELECT weekday, start_time::text, end_time::text, is_open\n\t\tFROM working_hours\n\t`)\n\tfor defRows.Next() {\n\t\tvar d DefaultHours\n\t\tif err := defRows.Scan(&d.Weekday, &d.StartTime, &d.EndTime, &d.IsOpen); err == nil {\n\t\t\tdefaultMap[d.Weekday] = d\n\t\t}\n\t}\n\tdefRows.Close()\n\n\t// Load exceptional applications for Mondays in range\n\tappRows, _ := db.DB.Query(r.Context(), `\n\t\tSELECT group_id, week_start\n\t\tFROM exceptional_group_applications\n\t\tWHERE week_start BETWEEN $1 AND $2\n\t`, start, end)\n\ttype appEntry struct {\n\t\tGroupID int\n\t\tWeekStart time.Time\n\t}\n\tapps := []appEntry{}\n\tgroupIDs := []int{}\n\tfor appRows.Next() {\n\t\tvar a appEntry\n\t\tif err := appRows.Scan(&a.GroupID, &a.WeekStart); err == nil {\n\t\t\ta.WeekStart = time.Date(a.WeekStart.Year(), a.WeekStart.Month(), a.WeekStart.Day(), 0, 0, 0, 0, ukLocation)\n\t\t\tapps = append(apps, a)\n\t\t\tgroupIDs = append(groupIDs, a.GroupID)\n\t\t}\n\t}\n\tappRows.Close()\n\n\texHoursMap := map[int]map[int]ExceptionalHours{}\n\tif len(groupIDs) > 0 {\n\t\tquery, args, _ := sqlIn(\"SELECT group_id, weekday, start_time::text, end_time::text, is_open FROM exceptional_working_hours WHERE group_id IN (%s)\", groupIDs)\n\t\trows, _ := db.DB.Query(r.Context(), query, args...)\n\t\tfor rows.Next() {\n\t\t\tvar h ExceptionalHours\n\t\t\tif err := rows.Scan(&h.GroupID, &h.Weekday, &h.StartTime, &h.EndTime, &h.IsOpen); err == nil {\n\t\t\t\tif _, ok := exHoursMap[h.GroupID]; !ok {\n\t\t\t\t\texHoursMap[h.GroupID] = map[int]ExceptionalHours{}\n\t\t\t\t}\n\t\t\t\texHoursMap[h.GroupID][h.Weekday] = h\n\t\t\t}\n\t\t}\n\t\trows.Close()\n\t}\n\n\t// Generate final result per day\n\tvar results []DayWorkingHours\n\tfor d := start; !d.After(end); d = d.AddDate(0, 0, 1) {\n\t\tweekday := int(d.Weekday())\n\t\tif weekday == 0 {\n\t\t\tweekday = 6 // Go Sunday=0 -> our Sunday=6\n\t\t} else {\n\t\t\tweekday -= 1\n\t\t}\n\n\t\t// Calculate the Monday of this week\n\t\tdaysSinceMonday := int(d.Weekday()) - 1\n\t\tif daysSinceMonday < 0 {\n\t\t\tdaysSinceMonday = 6 // Sunday\n\t\t}\n\t\tweekStart := d.AddDate(0, 0, -daysSinceMonday)\n\t\tweekStart = time.Date(weekStart.Year(), weekStart.Month(), weekStart.Day(), 0, 0, 0, 0, ukLocation)\n\n\t\tvar applied *ExceptionalHours\n\t\tweekStartStr := weekStart.Format(\"2006-01-02\")\n\t\tfor _, a := range apps {\n\t\t\tappWeekStartStr := a.WeekStart.Format(\"2006-01-02\")\n\t\t\tif appWeekStartStr == weekStartStr {\n\t\t\t\tif dayHours, ok := exHoursMap[a.GroupID][weekday]; ok {\n\t\t\t\t\tapplied = &dayHours\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tvar day DayWorkingHours\n\t\tday.Date = d.Format(\"2006-01-02\")\n\t\tday.Weekday = weekday\n\n\t\tif applied != nil {\n\t\t\tday.StartTime = applied.StartTime\n\t\t\tday.EndTime = applied.EndTime\n\t\t\tday.IsOpen = applied.IsOpen\n\t\t\tday.Source = \"exceptional\"\n\t\t} else if def, ok := defaultMap[weekday]; ok {\n\t\t\tday.StartTime = def.StartTime\n\t\t\tday.EndTime = def.EndTime\n\t\t\tday.IsOpen = def.IsOpen\n\t\t\tday.Source = \"default\"\n\t\t} else {\n\t\t\tday.StartTime = \"00:00\"\n\t\t\tday.EndTime = \"00:00\"\n\t\t\tday.IsOpen = false\n\t\t\tday.Source = \"default\"\n\t\t}\n\n\t\tresults = append(results, day)\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(results)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/default-hours.go\nType: func\nName: sqlIn\nDoc:\n--- helper: sqlIn generates IN queries dynamically for Postgres ---\n\nCode:\nfunc sqlIn(query string, args []int) (string, []interface{}, error) {\n\tinArgs := []interface{}{}\n\tplaceholders := \"\"\n\tfor i, arg := range args {\n\t\tif i > 0 {\n\t\t\tplaceholders += \",\"\n\t\t}\n\t\tplaceholders += fmt.Sprintf(\"$%d\", i+1)\n\t\tinArgs = append(inArgs, arg)\n\t}\n\tquery = fmt.Sprintf(query, placeholders)\n\treturn query, inArgs, nil\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/default-hours.go\nType: struct\nName: TimeSlot\nDoc:\n--- Types for Available Hours ---\n\nCode:\ntype TimeSlot struct {\n\tStartTime string `json:\"startTime\"`\n\tEndTime string `json:\"endTime\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/default-hours.go\nType: struct\nName: DayAvailableHours\nCode:\ntype DayAvailableHours struct {\n\tDate string `json:\"date\"`\n\tWeekday int `json:\"weekday\"`\n\tIsOpen bool `json:\"isOpen\"`\n\tSlots []TimeSlot `json:\"slots\"`\n\tSource string `json:\"source\"`\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/default-hours.go\nType: func\nName: GetAvailableHours\nDoc:\n--- GetAvailableHours (with bookings, UK-local) ---\n\nCode:\nfunc GetAvailableHours(w http.ResponseWriter, r *http.Request) {\n\tstartStr := r.URL.Query().Get(\"start\")\n\tendStr := r.URL.Query().Get(\"end\")\n\tif startStr == \"\" || endStr == \"\" {\n\t\thttp.Error(w, \"start and end query params required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tukLocation, _ := time.LoadLocation(\"Europe/London\")\n\tstart, _ := time.ParseInLocation(\"2006-01-02\", startStr, ukLocation)\n\tend, _ := time.ParseInLocation(\"2006-01-02\", endStr, ukLocation)\n\n\t// set start/end of day\n\tstart = time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, ukLocation)\n\tend = time.Date(end.Year(), end.Month(), end.Day(), 23, 59, 59, 999999999, ukLocation)\n\n\t// Load default hours\n\tdefaultMap := map[int]DefaultHours{}\n\tdefRows, _ := db.DB.Query(r.Context(), `SELECT weekday, start_time::text, end_time::text, is_open FROM working_hours`)\n\tfor defRows.Next() {\n\t\tvar d DefaultHours\n\t\tif err := defRows.Scan(&d.Weekday, &d.StartTime, &d.EndTime, &d.IsOpen); err == nil {\n\t\t\tdefaultMap[d.Weekday] = d\n\t\t}\n\t}\n\tdefRows.Close()\n\n\t// Load exceptional applications for Mondays in range\n\tappRows, _ := db.DB.Query(r.Context(), `\n\t\tSELECT group_id, week_start\n\t\tFROM exceptional_group_applications\n\t\tWHERE week_start BETWEEN $1 AND $2\n\t`, start, end)\n\ttype appEntry struct {\n\t\tGroupID int\n\t\tWeekStart time.Time\n\t}\n\tapps := []appEntry{}\n\tgroupIDs := []int{}\n\tfor appRows.Next() {\n\t\tvar a appEntry\n\t\tif err := appRows.Scan(&a.GroupID, &a.WeekStart); err == nil {\n\t\t\ta.WeekStart = time.Date(a.WeekStart.Year(), a.WeekStart.Month(), a.WeekStart.Day(), 0, 0, 0, 0, ukLocation)\n\t\t\tapps = append(apps, a)\n\t\t\tgroupIDs = append(groupIDs, a.GroupID)\n\t\t}\n\t}\n\tappRows.Close()\n\n\texHoursMap := map[int]map[int]ExceptionalHours{}\n\tif len(groupIDs) > 0 {\n\t\tquery, args, _ := sqlIn(\"SELECT group_id, weekday, start_time::text, end_time::text, is_open FROM exceptional_working_hours WHERE group_id IN (%s)\", groupIDs)\n\t\trows, _ := db.DB.Query(r.Context(), query, args...)\n\t\tfor rows.Next() {\n\t\t\tvar h ExceptionalHours\n\t\t\tif err := rows.Scan(&h.GroupID, &h.Weekday, &h.StartTime, &h.EndTime, &h.IsOpen); err == nil {\n\t\t\t\tif _, ok := exHoursMap[h.GroupID]; !ok {\n\t\t\t\t\texHoursMap[h.GroupID] = map[int]ExceptionalHours{}\n\t\t\t\t}\n\t\t\t\texHoursMap[h.GroupID][h.Weekday] = h\n\t\t\t}\n\t\t}\n\t\trows.Close()\n\t}\n\n\t// Load bookings\n\tbookingRows, _ := db.DB.Query(r.Context(), `\n\t\tSELECT \n\t\t\tb.start_time, \n\t\t\tCOALESCE(SUM(\n\t\t\t\tCASE \n\t\t\t\t\tWHEN bs.override_duration_minutes IS NOT NULL AND bs.override_duration_minutes > 0 \n\t\t\t\t\t\tTHEN bs.override_duration_minutes \n\t\t\t\t\tELSE s.duration_minutes \n\t\t\t\tEND\n\t\t\t), 0) AS total_duration\n\t\tFROM bookings b\n\t\tLEFT JOIN booking_services bs ON b.id = bs.booking_id\n\t\tLEFT JOIN services s ON bs.service_id = s.id\n\t\tWHERE b.start_time >= $1 AND b.start_time <= $2\n\t\tGROUP BY b.id, b.start_time\n\t\tORDER BY b.start_time\n\t`, start, end)\n\n\tbookings := map[string][]TimeSlot{}\n\tfor bookingRows.Next() {\n\t\tvar t time.Time\n\t\tvar dur int\n\t\tif err := bookingRows.Scan(&t, &dur); err == nil {\n\t\t\tt = t.In(ukLocation)\n\t\t\tdateStr := t.Format(\"2006-01-02\")\n\t\t\tendTime := t.Add(time.Duration(dur) * time.Minute)\n\t\t\tbookings[dateStr] = append(bookings[dateStr], TimeSlot{\n\t\t\t\tStartTime: t.Format(\"15:04\"),\n\t\t\t\tEndTime: endTime.Format(\"15:04\"),\n\t\t\t})\n\t\t}\n\t}\n\tbookingRows.Close()\n\n\t// Generate available slots per day\n\tvar results []DayAvailableHours\n\tfor d := start; !d.After(end); d = d.AddDate(0, 0, 1) {\n\t\tweekday := int(d.Weekday())\n\t\tif weekday == 0 {\n\t\t\tweekday = 6\n\t\t} else {\n\t\t\tweekday -= 1\n\t\t}\n\n\t\t// Calculate the Monday of this week\n\t\tdaysSinceMonday := int(d.Weekday()) - 1\n\t\tif daysSinceMonday < 0 {\n\t\t\tdaysSinceMonday = 6 // Sunday\n\t\t}\n\t\tweekStart := d.AddDate(0, 0, -daysSinceMonday)\n\t\tweekStart = time.Date(weekStart.Year(), weekStart.Month(), weekStart.Day(), 0, 0, 0, 0, ukLocation)\n\n\t\tvar applied *ExceptionalHours\n\t\tweekStartStr := weekStart.Format(\"2006-01-02\")\n\t\tfor _, a := range apps {\n\t\t\tappWeekStartStr := a.WeekStart.Format(\"2006-01-02\")\n\t\t\tif appWeekStartStr == weekStartStr {\n\t\t\t\tif dayHours, ok := exHoursMap[a.GroupID][weekday]; ok {\n\t\t\t\t\tapplied = &dayHours\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tvar day DayAvailableHours\n\t\tday.Date = d.Format(\"2006-01-02\")\n\t\tday.Weekday = weekday\n\n\t\tvar baseStart, baseEnd string\n\t\tvar isOpen bool\n\t\tif applied != nil {\n\t\t\tbaseStart = applied.StartTime\n\t\t\tbaseEnd = applied.EndTime\n\t\t\tisOpen = applied.IsOpen\n\t\t\tday.Source = \"exceptional\"\n\t\t} else if def, ok := defaultMap[weekday]; ok {\n\t\t\tbaseStart = def.StartTime\n\t\t\tbaseEnd = def.EndTime\n\t\t\tisOpen = def.IsOpen\n\t\t\tday.Source = \"default\"\n\t\t} else {\n\t\t\tbaseStart = \"00:00\"\n\t\t\tbaseEnd = \"00:00\"\n\t\t\tisOpen = false\n\t\t\tday.Source = \"default\"\n\t\t}\n\n\t\tday.IsOpen = isOpen\n\t\tif isOpen {\n\t\t\tslots := []TimeSlot{{StartTime: baseStart, EndTime: baseEnd}}\n\t\t\tif booked, ok := bookings[day.Date]; ok {\n\t\t\t\tslots = subtractTimeSlots(slots, booked)\n\t\t\t}\n\t\t\tday.Slots = slots\n\t\t} else {\n\t\t\tday.Slots = []TimeSlot{}\n\t\t}\n\n\t\tresults = append(results, day)\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(results)\n}\n", + "File: /home/popertots/Crussell/backend/handlers/scheduling/default-hours.go\nType: func\nName: subtractTimeSlots\nDoc:\nsubtractTimeSlots removes gaps from available slots\nReturns the remaining available time slots after removing the gaps\n\nCode:\nfunc subtractTimeSlots(available []TimeSlot, gaps []TimeSlot) []TimeSlot {\n\tif len(gaps) == 0 {\n\t\treturn available\n\t}\n\n\tresult := []TimeSlot{}\n\n\tfor _, slot := range available {\n\t\tcurrent := []TimeSlot{slot}\n\n\t\t// Apply each gap\n\t\tfor _, gap := range gaps {\n\t\t\tvar temp []TimeSlot\n\t\t\tfor _, s := range current {\n\t\t\t\t// Check if gap overlaps with this slot\n\t\t\t\tif gap.EndTime <= s.StartTime || gap.StartTime >= s.EndTime {\n\t\t\t\t\t// No overlap, keep the slot as is\n\t\t\t\t\ttemp = append(temp, s)\n\t\t\t\t} else {\n\t\t\t\t\t// Overlap exists, split the slot\n\t\t\t\t\tif s.StartTime < gap.StartTime {\n\t\t\t\t\t\t// Keep the part before the gap\n\t\t\t\t\t\ttemp = append(temp, TimeSlot{\n\t\t\t\t\t\t\tStartTime: s.StartTime,\n\t\t\t\t\t\t\tEndTime: gap.StartTime,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tif gap.EndTime < s.EndTime {\n\t\t\t\t\t\t// Keep the part after the gap\n\t\t\t\t\t\ttemp = append(temp, TimeSlot{\n\t\t\t\t\t\t\tStartTime: gap.EndTime,\n\t\t\t\t\t\t\tEndTime: s.EndTime,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcurrent = temp\n\t\t}\n\n\t\tresult = append(result, current...)\n\t}\n\n\treturn result\n}\n", + "File: /home/popertots/Crussell/backend/handlers/admin/analytics.go\nLanguage: go\n\npackage admin", + "File: /home/popertots/Crussell/backend/handlers/admin/bookings.go\nLanguage: go\n\npackage admin", + "File: /home/popertots/Crussell/backend/handlers/admin/users.go\nLanguage: go\n\npackage admin", + "File: /home/popertots/Crussell/backend/internal/dav/service_dev.go\nType: var\nName: Service\nCode:\nvar Service *BaseService\n", + "File: /home/popertots/Crussell/backend/internal/dav/service_dev.go\nType: func\nName: init\nCode:\nfunc init() {\n\tif err := connect(); err != nil {\n\t\tlog.Fatalf(\"failed to initialize dev service: %v\", err)\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/service_dev.go\nType: func\nName: connect\nCode:\nfunc connect() error {\n\tdsn := fmt.Sprintf(\n\t\t\"postgres://%s:%s@localhost:5432/%s?sslmode=disable\",\n\t\tgetEnv(\"POSTGRES_USER\"),\n\t\tgetEnv(\"POSTGRES_PASSWORD\"),\n\t\tgetEnv(\"POSTGRES_DB\"),\n\t)\n\tpool, err := pgxpool.New(context.Background(), dsn)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tService = newBaseService(pool)\n\treturn testDB(pool)\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/service_dev.go\nType: func\nName: testDB\nCode:\nfunc testDB(pool *pgxpool.Pool) error {\n\tvar n int\n\terr := pool.QueryRow(context.Background(), \"SELECT 1\").Scan(&n)\n\tif err != nil || n != 1 {\n\t\treturn fmt.Errorf(\"db test failed: %w\", err)\n\t}\n\treturn nil\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/service_dev.go\nType: func\nName: getEnv\nCode:\nfunc getEnv(key string) string {\n\tif v := os.Getenv(key); v != \"\" {\n\t\treturn v\n\t}\n\tlog.Fatalf(\"FATAL: environment variable %s not set\", key)\n\treturn \"\"\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/types.go\nType: struct\nName: Calendar\nDoc:\n============================================================================\nCalendar Types\n============================================================================\n\nCode:\ntype Calendar struct {\n\tID int `json:\"id\"`\n\tPrincipalURI string `json:\"principaluri\"`\n\tDisplayName string `json:\"displayname\"`\n\tURI string `json:\"uri\"`\n\tDescription string `json:\"description\"`\n\tCalendarOrder int `json:\"calendarorder\"`\n\tCalendarColor string `json:\"calendarcolor\"`\n\tComponents string `json:\"components\"`\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/types.go\nType: struct\nName: CalendarEvent\nCode:\ntype CalendarEvent struct {\n\tID int `json:\"id\"`\n\tCalendarID int `json:\"calendarid\"`\n\tURI string `json:\"uri\"`\n\tCalendarData string `json:\"calendardata\"`\n\tLastModified int64 `json:\"lastmodified\"`\n\tEtag string `json:\"etag\"`\n\tSize int `json:\"size\"`\n\tComponentType string `json:\"componenttype\"`\n\tFirstOccurence int64 `json:\"firstoccurence\"`\n\tLastOccurence int64 `json:\"lastoccurence\"`\n\tUID string `json:\"uid\"`\n\tContactURIs []string `json:\"contact_uris\"` // Extracted from ATTENDEE fields\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/types.go\nType: struct\nName: EventInput\nDoc:\nExtracted from ATTENDEE fields\n\nCode:\ntype EventInput struct {\n\tSummary string\n\tDescription string\n\tLocation string\n\tStart time.Time\n\tEnd time.Time\n\tAllDay bool\n\tContactURIs []string // URIs of contacts to attach as attendees\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/types.go\nType: struct\nName: AddressBook\nDoc:\n============================================================================\nCardDAV Types\n============================================================================\n\nCode:\ntype AddressBook struct {\n\tID int `json:\"id\"`\n\tPrincipalURI string `json:\"principaluri\"`\n\tDisplayName string `json:\"displayname\"`\n\tURI string `json:\"uri\"`\n\tDescription string `json:\"description\"`\n\tSyncToken int `json:\"synctoken\"`\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/types.go\nType: struct\nName: Contact\nCode:\ntype Contact struct {\n\tID int `json:\"id\"`\n\tAddressBookID int `json:\"addressbookid\"`\n\tURI string `json:\"uri\"`\n\tCardData string `json:\"carddata\"`\n\tLastModified int64 `json:\"lastmodified\"`\n\tEtag string `json:\"etag\"`\n\tSize int `json:\"size\"`\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/types.go\nType: struct\nName: ContactInput\nCode:\ntype ContactInput struct {\n\tUserID string\n\tFirstName string\n\tLastName string\n\tEmail string\n\tPhone string\n\tDOB string\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/types.go\nType: func\nName: GenerateICalEvent\nDoc:\nGenerateICalEvent creates iCalendar format for UK timezone\n\nCode:\nfunc GenerateICalEvent(input EventInput) string {\n\tuid := fmt.Sprintf(\"%d@example.com\", time.Now().UnixNano())\n\tdtstamp := time.Now().UTC().Format(\"20060102T150405Z\")\n\n\tvar dtstart, dtend string\n\tif input.AllDay {\n\t\tdtstart = fmt.Sprintf(\"DTSTART;VALUE=DATE:%s\", input.Start.Format(\"20060102\"))\n\t\tdtend = fmt.Sprintf(\"DTEND;VALUE=DATE:%s\", input.End.Format(\"20060102\"))\n\t} else {\n\t\tdtstart = fmt.Sprintf(\"DTSTART;TZID=Europe/London:%s\", input.Start.Format(\"20060102T150405\"))\n\t\tdtend = fmt.Sprintf(\"DTEND;TZID=Europe/London:%s\", input.End.Format(\"20060102T150405\"))\n\t}\n\n\t// Build attendees section\n\tattendees := \"\"\n\tfor _, contactURI := range input.ContactURIs {\n\t\tattendees += fmt.Sprintf(\"ATTENDEE;CN=%s:%s\\n\", contactURI, contactURI)\n\t}\n\n\tical := fmt.Sprintf(`BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//Your App//EN\nCALSCALE:GREGORIAN\nBEGIN:VTIMEZONE\nTZID:Europe/London\nBEGIN:DAYLIGHT\nTZOFFSETFROM:+0000\nTZOFFSETTO:+0100\nTZNAME:BST\nDTSTART:19700329T010000\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\nEND:DAYLIGHT\nBEGIN:STANDARD\nTZOFFSETFROM:+0100\nTZOFFSETTO:+0000\nTZNAME:GMT\nDTSTART:19701025T020000\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\nEND:STANDARD\nEND:VTIMEZONE\nBEGIN:VEVENT\nUID:%s\nDTSTAMP:%s\n%s\n%s\nSUMMARY:%s\nDESCRIPTION:%s\nLOCATION:%s\n%sSEQUENCE:0\nSTATUS:CONFIRMED\nTRANSP:OPAQUE\nEND:VEVENT\nEND:VCALENDAR`, uid, dtstamp, dtstart, dtend,\n\t\tescapeICalText(input.Summary),\n\t\tescapeICalText(input.Description),\n\t\tescapeICalText(input.Location),\n\t\tattendees)\n\n\treturn ical\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/types.go\nType: func\nName: GenerateVCard\nDoc:\nGenerateVCard creates vCard format (version 3.0)\n\nCode:\nfunc GenerateVCard(input ContactInput) string {\n\tvcard := fmt.Sprintf(`BEGIN:VCARD\nVERSION:3.0\nUID:%s\nFN:%s %s\nN:%s;%s;;;\nEMAIL;TYPE=INTERNET:%s\nTEL;TYPE=CELL:%s\nBDAY:%s\nREV:%s\nEND:VCARD`,\n\t\tinput.UserID,\n\t\tinput.FirstName, input.LastName,\n\t\tinput.LastName, input.FirstName,\n\t\tinput.Email,\n\t\tinput.Phone,\n\t\tinput.DOB,\n\t\ttime.Now().UTC().Format(\"20060102T150405Z\"))\n\n\treturn vcard\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/types.go\nType: func\nName: escapeICalText\nCode:\nfunc escapeICalText(text string) string {\n\ttext = replaceAll(text, \"\\\\\", \"\\\\\\\\\")\n\ttext = replaceAll(text, \"\\n\", \"\\\\n\")\n\ttext = replaceAll(text, \",\", \"\\\\,\")\n\ttext = replaceAll(text, \";\", \"\\\\;\")\n\treturn text\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/types.go\nType: func\nName: replaceAll\nCode:\nfunc replaceAll(s, old, new string) string {\n\tresult := \"\"\n\tfor _, char := range s {\n\t\tif string(char) == old {\n\t\t\tresult += new\n\t\t} else {\n\t\t\tresult += string(char)\n\t\t}\n\t}\n\treturn result\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/service_prod.go\nType: var\nName: Service\nCode:\nvar Service *BaseService\n", + "File: /home/popertots/Crussell/backend/internal/dav/service_prod.go\nType: func\nName: init\nCode:\nfunc init() {\n\tif err := connect(); err != nil {\n\t\tlog.Fatalf(\"failed to initialize prod service: %v\", err)\n\t}\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/service_prod.go\nType: func\nName: connect\nCode:\nfunc connect() error {\n\tdsn := fmt.Sprintf(\n\t\t\"postgres://%s:%s@%s:5432/%s\",\n\t\tgetEnv(\"POSTGRES_USER\"),\n\t\tgetEnv(\"POSTGRES_PASSWORD\"),\n\t\tgetEnv(\"POSTGRES_HOST\"),\n\t\tgetEnv(\"POSTGRES_DB\"),\n\t)\n\tpool, err := pgxpool.New(context.Background(), dsn)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tService = newBaseService(pool)\n\treturn testDB(pool)\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/service_prod.go\nType: func\nName: testDB\nCode:\nfunc testDB(pool *pgxpool.Pool) error {\n\tvar n int\n\terr := pool.QueryRow(context.Background(), \"SELECT 1\").Scan(&n)\n\tif err != nil || n != 1 {\n\t\treturn fmt.Errorf(\"db test failed: %w\", err)\n\t}\n\treturn nil\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/service_prod.go\nType: func\nName: getEnv\nCode:\nfunc getEnv(key string) string {\n\tif v := os.Getenv(key); v != \"\" {\n\t\treturn v\n\t}\n\tlog.Fatalf(\"FATAL: environment variable %s not set\", key)\n\treturn \"\"\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: struct\nName: BaseService\nDoc:\nBaseService holds shared DB connection\n\nCode:\ntype BaseService struct {\n\tdb *pgxpool.Pool\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: func\nName: newBaseService\nDoc:\nnewBaseService returns a new BaseService instance\n\nCode:\nfunc newBaseService(db *pgxpool.Pool) *BaseService {\n\treturn &BaseService{db: db}\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: ListEventsForMonth\nDoc:\n============================================================================\nCalendar Functions\n============================================================================\n\nCode:\nfunc (s *BaseService) ListEventsForMonth(year int, month time.Month) ([]CalendarEvent, error) {\n\tstart := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC)\n\tend := start.AddDate(0, 1, 0).Add(-time.Second)\n\n\tquery := `\n\t\tSELECT id, calendarid, uri, calendardata, lastmodified, etag, size, componenttype,\n\t\t firstoccurence, lastoccurence, uid\n\t\tFROM dav_calendarobjects\n\t\tWHERE firstoccurence >= $1 AND firstoccurence <= $2\n\t\tORDER BY firstoccurence\n\t`\n\treturn s.queryEventsWithContacts(query, start.Unix(), end.Unix())\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: ListEventsBetween\nCode:\nfunc (s *BaseService) ListEventsBetween(start, end time.Time) ([]CalendarEvent, error) {\n\tquery := `\n\t\tSELECT id, calendarid, uri, calendardata, lastmodified, etag, size, componenttype,\n\t\t firstoccurence, lastoccurence, uid\n\t\tFROM dav_calendarobjects\n\t\tWHERE firstoccurence >= $1 AND firstoccurence <= $2\n\t\tORDER BY firstoccurence\n\t`\n\treturn s.queryEventsWithContacts(query, start.Unix(), end.Unix())\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: ListEventsTomorrow\nCode:\nfunc (s *BaseService) ListEventsTomorrow() ([]CalendarEvent, error) {\n\tnow := time.Now()\n\ttomorrow := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location())\n\tdayAfter := tomorrow.Add(24 * time.Hour)\n\treturn s.ListEventsBetween(tomorrow, dayAfter)\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: ListEventsThisWeek\nCode:\nfunc (s *BaseService) ListEventsThisWeek() ([]CalendarEvent, error) {\n\tnow := time.Now()\n\tweekday := int(now.Weekday())\n\tif weekday == 0 { // Sunday\n\t\tweekday = 7\n\t}\n\tmonday := now.AddDate(0, 0, -weekday+1)\n\tmonday = time.Date(monday.Year(), monday.Month(), monday.Day(), 0, 0, 0, 0, now.Location())\n\tsunday := monday.AddDate(0, 0, 7)\n\treturn s.ListEventsBetween(monday, sunday)\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: GetContactByURI\nDoc:\n============================================================================\nContact Functions\n============================================================================\n\nCode:\nfunc (s *BaseService) GetContactByURI(addressBookID int, uri string) (*Contact, error) {\n\tquery := `\n\t\tSELECT id, addressbookid, uri, carddata, lastmodified, etag, size\n\t\tFROM dav_cards\n\t\tWHERE addressbookid = $1 AND uri = $2\n\t`\n\tvar c Contact\n\terr := s.db.QueryRow(context.Background(), query, addressBookID, uri).Scan(\n\t\t&c.ID, &c.AddressBookID, &c.URI, &c.CardData, &c.LastModified, &c.Etag, &c.Size,\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"contact not found: %w\", err)\n\t}\n\treturn &c, nil\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: ListAllContacts\nCode:\nfunc (s *BaseService) ListAllContacts() ([]Contact, error) {\n\tquery := `SELECT id, addressbookid, uri, carddata, lastmodified, etag, size FROM dav_cards ORDER BY lastmodified DESC`\n\trows, err := s.db.Query(context.Background(), query)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tvar contacts []Contact\n\tfor rows.Next() {\n\t\tvar c Contact\n\t\tif err := rows.Scan(&c.ID, &c.AddressBookID, &c.URI, &c.CardData, &c.LastModified, &c.Etag, &c.Size); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcontacts = append(contacts, c)\n\t}\n\treturn contacts, nil\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: ListRecentContacts\nCode:\nfunc (s *BaseService) ListRecentContacts(days int) ([]Contact, error) {\n\tcutoff := time.Now().AddDate(0, 0, -days).Unix()\n\tquery := `SELECT id, addressbookid, uri, carddata, lastmodified, etag, size FROM dav_cards WHERE lastmodified >= $1 ORDER BY lastmodified DESC`\n\trows, err := s.db.Query(context.Background(), query, cutoff)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tvar contacts []Contact\n\tfor rows.Next() {\n\t\tvar c Contact\n\t\tif err := rows.Scan(&c.ID, &c.AddressBookID, &c.URI, &c.CardData, &c.LastModified, &c.Etag, &c.Size); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcontacts = append(contacts, c)\n\t}\n\treturn contacts, nil\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: queryEventsWithContacts\nDoc:\n============================================================================\nHelper Methods\n============================================================================\n\nCode:\nfunc (s *BaseService) queryEventsWithContacts(query string, args ...interface{}) ([]CalendarEvent, error) {\n\trows, err := s.db.Query(context.Background(), query, args...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tvar events []CalendarEvent\n\tfor rows.Next() {\n\t\tvar e CalendarEvent\n\t\tif err := rows.Scan(\n\t\t\t&e.ID, &e.CalendarID, &e.URI, &e.CalendarData, &e.LastModified, &e.Etag, &e.Size,\n\t\t\t&e.ComponentType, &e.FirstOccurence, &e.LastOccurence, &e.UID,\n\t\t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\te.ContactURIs = extractContactURIsFromICalendar(e.CalendarData)\n\t\tevents = append(events, e)\n\t}\n\treturn events, nil\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: func\nName: extractContactURIsFromICalendar\nCode:\nfunc extractContactURIsFromICalendar(icalData string) []string {\n\tvar uris []string\n\tfor _, line := range strings.Split(icalData, \"\\n\") {\n\t\tline = strings.TrimSpace(line)\n\t\tif strings.HasPrefix(line, \"ATTENDEE\") {\n\t\t\tparts := strings.Split(line, \":\")\n\t\t\tif len(parts) >= 2 {\n\t\t\t\turis = append(uris, strings.TrimSpace(parts[len(parts)-1]))\n\t\t\t}\n\t\t}\n\t}\n\treturn uris\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: CreateContact\nDoc:\nCreateContact adds a new contact to an address book\n\nCode:\nfunc (s *BaseService) CreateContact(addressBookID int, userID string, input ContactInput) error {\n\tnow := time.Now().Unix()\n\turi := fmt.Sprintf(\"%s.vcf\", userID)\n\tcardData := GenerateVCard(input)\n\n\tquery := `\n\t\tINSERT INTO dav_cards (addressbookid, uri, carddata, lastmodified, etag, size)\n\t\tVALUES ($1, $2, $3, $4, $5, $6)\n\t`\n\t_, err := s.db.Exec(context.Background(), query,\n\t\taddressBookID,\n\t\turi,\n\t\tcardData,\n\t\tnow,\n\t\tfmt.Sprintf(\"%d\", now),\n\t\tlen(cardData),\n\t)\n\treturn err\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: UpdateContact\nDoc:\nUpdateContact updates an existing contact by URI\n\nCode:\nfunc (s *BaseService) UpdateContact(addressBookID int, uri string, input ContactInput) error {\n\tnow := time.Now().Unix()\n\tcardData := GenerateVCard(input)\n\tquery := `\n\t\tUPDATE dav_cards\n\t\tSET carddata = $1, lastmodified = $2, etag = $3, size = $4\n\t\tWHERE addressbookid = $5 AND uri = $6\n\t`\n\t_, err := s.db.Exec(context.Background(), query, cardData, now, fmt.Sprintf(\"%d\", now), len(cardData), addressBookID, uri)\n\treturn err\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: DeleteContact\nDoc:\nDeleteContact deletes a contact by URI\n\nCode:\nfunc (s *BaseService) DeleteContact(addressBookID int, uri string) error {\n\tquery := `DELETE FROM dav_cards WHERE addressbookid = $1 AND uri = $2`\n\t_, err := s.db.Exec(context.Background(), query, addressBookID, uri)\n\treturn err\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: CreateEvent\nDoc:\nCreateEvent adds a new event to a calendar\n\nCode:\nfunc (s *BaseService) CreateEvent(calendarID int, input EventInput) error {\n\tuid := fmt.Sprintf(\"%d@example.com\", time.Now().UnixNano())\n\tnow := time.Now().Unix()\n\tcalendarData := GenerateICalEvent(input)\n\n\tquery := `\n\t\tINSERT INTO dav_calendarobjects\n\t\t\t(calendarid, uri, calendardata, lastmodified, etag, size, componenttype,\n\t\t\t firstoccurence, lastoccurence, uid)\n\t\tVALUES ($1, $2, $3, $4, $5, $6, 'VEVENT', $7, $8, $9)\n\t`\n\t_, err := s.db.Exec(context.Background(), query,\n\t\tcalendarID,\n\t\tuid+\".ics\",\n\t\tcalendarData,\n\t\tnow,\n\t\tfmt.Sprintf(\"%d\", now),\n\t\tlen(calendarData),\n\t\tinput.Start.Unix(),\n\t\tinput.End.Unix(),\n\t\tuid,\n\t)\n\treturn err\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: UpdateEvent\nDoc:\nUpdateEvent updates an existing calendar event by UID\n\nCode:\nfunc (s *BaseService) UpdateEvent(calendarID int, uid string, input EventInput) error {\n\tnow := time.Now().Unix()\n\tcalendarData := GenerateICalEvent(input)\n\n\tquery := `\n\t\tUPDATE dav_calendarobjects\n\t\tSET calendardata = $1, lastmodified = $2, etag = $3, size = $4,\n\t\t firstoccurence = $5, lastoccurence = $6\n\t\tWHERE calendarid = $7 AND uid = $8\n\t`\n\t_, err := s.db.Exec(context.Background(), query,\n\t\tcalendarData, now, fmt.Sprintf(\"%d\", now), len(calendarData),\n\t\tinput.Start.Unix(), input.End.Unix(),\n\t\tcalendarID, uid,\n\t)\n\treturn err\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: DeleteEvent\nDoc:\nDeleteEvent deletes an event by UID\n\nCode:\nfunc (s *BaseService) DeleteEvent(calendarID int, uid string) error {\n\tquery := `DELETE FROM dav_calendarobjects WHERE calendarid = $1 AND uid = $2`\n\t_, err := s.db.Exec(context.Background(), query, calendarID, uid)\n\treturn err\n}\n", + "File: /home/popertots/Crussell/backend/internal/dav/shared.go\nType: method (*BaseService)\nName: ListEventsForContact\nDoc:\nListEventsForContactSQL returns all calendar events where the given contact URI is an attendee (SQL optimized)\n\nCode:\nfunc (s *BaseService) ListEventsForContact(contactURI string) ([]CalendarEvent, error) {\n\t// Use pattern matching to find events containing the contact URI in ATTENDEE lines\n\tlikePattern := \"%\" + contactURI + \"%\"\n\n\tquery := `\n\t\tSELECT id, calendarid, uri, calendardata, lastmodified, etag, size, componenttype,\n\t\t firstoccurence, lastoccurence, uid\n\t\tFROM dav_calendarobjects\n\t\tWHERE calendardata LIKE $1\n\t\tORDER BY firstoccurence\n\t`\n\n\treturn s.queryEventsWithContacts(query, likePattern)\n}\n", + "File: /home/popertots/Crussell/obsidian/Crussell/.obsidian/plugins/obsidian-kanban/main.js\nLanguage: javascript\n\nvar sF=Object.create;var Vs=Object.defineProperty;var lF=Object.getOwnPropertyDescriptor;var uF=Object.getOwnPropertyNames;var cF=Object.getPrototypeOf,dF=Object.prototype.hasOwnProperty;var fF=(e,t,r)=>t in e?Vs(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var hF=(e,t)=>()=>(e&&(t=e(e=0)),t);var wn=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Rf=(e,t)=>{for(var r in t)Vs(e,r,{get:t[r],enumerable:!0})},av=(e,t,r,n)=>{if(t&&typeof t==\"object\"||typeof t==\"function\")for(let i of uF(t))!dF.call(e,i)&&i!==r&&Vs(e,i,{get:()=>t[i],enumerable:!(n=lF(t,i))||n.enumerable});return e};var Ct=(e,t,r)=>(r=e!=null?sF(cF(e)):{},av(t||!e||!e.__esModule?Vs(r,\"default\",{value:e,enumerable:!0}):r,e)),ov=e=>av(Vs({},\"__esModule\",{value:!0}),e);var ur=(e,t,r)=>(fF(e,typeof t!=\"symbol\"?t+\"\":t,r),r);var ln=wn((hi,Zv)=>{\"use strict\";Object.defineProperty(hi,\"__esModule\",{value:!0});function Qr(e){return typeof e==\"object\"&&!(\"toString\"in e)?Object.prototype.toString.call(e).slice(8,-1):e}var eI=typeof process==\"object\"&&!0;function Vr(e,t){if(!e)throw eI?new Error(\"Invariant failed\"):new Error(t())}hi.invariant=Vr;var nh=Object.prototype.hasOwnProperty,tI=Array.prototype.splice,nI=Object.prototype.toString;function Ua(e){return nI.call(e).slice(8,-1)}var bu=Object.assign||function(e,t){return rh(t).forEach(function(r){nh.call(t,r)&&(e[r]=t[r])}),e},rh=typeof Object.getOwnPropertySymbols==\"function\"?function(e){return Object.keys(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.keys(e)};function sa(e){return Array.isArray(e)?bu(e.constructor(e.length),e):Ua(e)===\"Map\"?new Map(e):Ua(e)===\"Set\"?new Set(e):e&&typeof e==\"object\"?bu(Object.create(Object.getPrototypeOf(e)),e):e}var Gv=function(){function e(){this.commands=bu({},rI),this.update=this.update.bind(this),this.update.extend=this.extend=this.extend.bind(this),this.update.isEquals=function(t,r){return t===r},this.update.newContext=function(){return new e().update}}return Object.defineProperty(e.prototype,\"isEquals\",{get:function(){return this.update.isEquals},set:function(t){this.update.isEquals=t},enumerable:!0,configurable:!0}),e.prototype.extend=function(t,r){this.commands[t]=r},e.prototype.update=function(t,r){var n=this,i=typeof r==\"function\"?{$apply:r}:r;Array.isArray(t)&&Array.isArray(i)||Vr(!Array.isArray(i),function(){return\"update(): You provided an invalid spec to update(). The spec may not contain an array except as the value of $set, $push, $unshift, $splice or any custom command allowing an array value.\"}),Vr(typeof i==\"object\"&&i!==null,function(){return\"update(): You provided an invalid spec to update(). The spec and every included key path must be plain objects containing one of the \"+(\"following commands: \"+Object.keys(n.commands).join(\", \")+\".\")});var a=t;return rh(i).forEach(function(o){if(nh.call(n.commands,o)){var s=t===a;a=n.commands[o](i[o],a,i,t),s&&n.isEquals(a,t)&&(a=t)}else{var u=Ua(t)===\"Map\"?n.update(t.get(o),i[o]):n.update(t[o],i[o]),l=Ua(a)===\"Map\"?a.get(o):a[o];(!n.isEquals(u,l)||typeof u==\"undefined\"&&!nh.call(t,o))&&(a===t&&(a=sa(t)),Ua(a)===\"Map\"?a.set(o,u):a[o]=u)}}),a},e}();hi.Context=Gv;var rI={$push:function(e,t,r){return jv(t,r,\"$push\"),e.length?t.concat(e):t},$unshift:function(e,t,r){return jv(t,r,\"$unshift\"),e.length?e.concat(t):t},$splice:function(e,t,r,n){return iI(t,r),e.forEach(function(i){Jv(i),t===n&&i.length&&(t=sa(n)),tI.apply(t,i)}),t},$set:function(e,t,r){return oI(r),e},$toggle:function(e,t){zs(e,\"$toggle\");var r=e.length?sa(t):t;return e.forEach(function(n){r[n]=!t[n]}),r},$unset:function(e,t,r,n){return zs(e,\"$unset\"),e.forEach(function(i){Object.hasOwnProperty.call(t,i)&&(t===n&&(t=sa(n)),delete t[i])}),t},$add:function(e,t,r,n){return qv(t,\"$add\"),zs(e,\"$add\"),Ua(t)===\"Map\"?e.forEach(function(i){var a=i[0],o=i[1];t===n&&t.get(a)!==o&&(t=sa(n)),t.set(a,o)}):e.forEach(function(i){t===n&&!t.has(i)&&(t=sa(n)),t.add(i)}),t},$remove:function(e,t,r,n){return qv(t,\"$remove\"),zs(e,\"$remove\"),e.forEach(function(i){t===n&&t.has(i)&&(t=sa(n)),t.delete(i)}),t},$merge:function(e,t,r,n){return sI(t,e),rh(e).forEach(function(i){e[i]!==t[i]&&(t===n&&(t=sa(n)),t[i]=e[i])}),t},$apply:function(e,t){return aI(e),e(t)}},ih=new Gv;hi.isEquals=ih.update.isEquals;hi.extend=ih.extend;hi.default=ih.update;hi.default.default=Zv.exports=bu(hi.default,hi);function jv(e,t,r){Vr(Array.isArray(e),function(){return\"update(): expected target of \"+Qr(r)+\" to be an array; got \"+Qr(e)+\".\"}),zs(t[r],r)}function zs(e,t){Vr(Array.isArray(e),function(){return\"update(): expected spec of \"+Qr(t)+\" to be an array; got \"+Qr(e)+\". Did you forget to wrap your parameter in an array?\"})}function iI(e,t){Vr(Array.isArray(e),function(){return\"Expected $splice target to be an array; got \"+Qr(e)}),Jv(t.$splice)}function Jv(e){Vr(Array.isArray(e),function(){return\"update(): expected spec of $splice to be an array of arrays; got \"+Qr(e)+\". Did you forget to wrap your parameters in an array?\"})}function aI(e){Vr(typeof e==\"function\",function(){return\"update(): expected spec of $apply to be a function; got \"+Qr(e)+\".\"})}function oI(e){Vr(Object.keys(e).length===1,function(){return\"Cannot have more than one key in an object with $set\"})}function sI(e,t){Vr(t&&typeof t==\"object\",function(){return\"update(): $merge expects a spec of type 'object'; got \"+Qr(t)}),Vr(e&&typeof e==\"object\",function(){return\"update(): $merge expects a target of type 'object'; got \"+Qr(e)})}function qv(e,t){var r=Ua(e);Vr(r===\"Map\"||r===\"Set\",function(){return\"update(): \"+Qr(t)+\" expects a target of type Set or Map; got \"+Qr(r)})}});var tw=wn((JB,ew)=>{\"use strict\";var lI=function(t){return uI(t)&&!cI(t)};function uI(e){return!!e&&typeof e==\"object\"}function cI(e){var t=Object.prototype.toString.call(e);return t===\"[object RegExp]\"||t===\"[object Date]\"||hI(e)}var dI=typeof Symbol==\"function\"&&Symbol.for,fI=dI?Symbol.for(\"react.element\"):60103;function hI(e){return e.$$typeof===fI}function mI(e){return Array.isArray(e)?[]:{}}function Ks(e,t){return t.clone!==!1&&t.isMergeableObject(e)?Mo(mI(e),e,t):e}function pI(e,t,r){return e.concat(t).map(function(n){return Ks(n,r)})}function gI(e,t){if(!t.customMerge)return Mo;var r=t.customMerge(e);return typeof r==\"function\"?r:Mo}function yI(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter(function(t){return Object.propertyIsEnumerable.call(e,t)}):[]}function Qv(e){return Object.keys(e).concat(yI(e))}function Xv(e,t){try{return t in e}catch(r){return!1}}function vI(e,t){return Xv(e,t)&&!(Object.hasOwnProperty.call(e,t)&&Object.propertyIsEnumerable.call(e,t))}function wI(e,t,r){var n={};return r.isMergeableObject(e)&&Qv(e).forEach(function(i){n[i]=Ks(e[i],r)}),Qv(t).forEach(function(i){vI(e,i)||(Xv(e,i)&&r.isMergeableObject(t[i])?n[i]=gI(i,r)(e[i],t[i],r):n[i]=Ks(t[i],r))}),n}function Mo(e,t,r){r=r||{},r.arrayMerge=r.arrayMerge||pI,r.isMergeableObject=r.isMergeableObject||lI,r.cloneUnlessOtherwiseSpecified=Ks;var n=Array.isArray(t),i=Array.isArray(e),a=n===i;return a?n?r.arrayMerge(e,t,r):wI(e,t,r):Ks(t,r)}Mo.all=function(t,r){if(!Array.isArray(t))throw new Error(\"first argument should be an array\");return t.reduce(function(n,i){return Mo(n,i,r)},{})};var bI=Mo;ew.exports=bI});var Db=wn((FU,Ch)=>{\"use strict\";var JA=Object.prototype.hasOwnProperty,cr=\"~\";function al(){}Object.create&&(al.prototype=Object.create(null),new al().__proto__||(cr=!1));function ZA(e,t,r){this.fn=e,this.context=t,this.once=r||!1}function bb(e,t,r,n,i){if(typeof r!=\"function\")throw new TypeError(\"The listener must be a function\");var a=new ZA(r,n||e,i),o=cr?cr+t:t;return e._events[o]?e._events[o].fn?e._events[o]=[e._events[o],a]:e._events[o].push(a):(e._events[o]=a,e._eventsCount++),e}function Ru(e,t){--e._eventsCount===0?e._events=new al:delete e._events[t]}function Xn(){this._events=new al,this._eventsCount=0}Xn.prototype.eventNames=function(){var t=[],r,n;if(this._eventsCount===0)return t;for(n in r=this._events)JA.call(r,n)&&t.push(cr?n.slice(1):n);return Object.getOwnPropertySymbols?t.concat(Object.getOwnPropertySymbols(r)):t};Xn.prototype.listeners=function(t){var r=cr?cr+t:t,n=this._events[r];if(!n)return[];if(n.fn)return[n.fn];for(var i=0,a=n.length,o=new Array(a);i{\"use strict\";\"use restrict\";var Ih=32;dn.INT_BITS=Ih;dn.INT_MAX=2147483647;dn.INT_MIN=-1<0)-(e<0)};dn.abs=function(e){var t=e>>Ih-1;return(e^t)-t};dn.min=function(e,t){return t^(e^t)&-(e65535)<<4,e>>>=t,r=(e>255)<<3,e>>>=r,t|=r,r=(e>15)<<2,e>>>=r,t|=r,r=(e>3)<<1,e>>>=r,t|=r,t|e>>1};dn.log10=function(e){return e>=1e9?9:e>=1e8?8:e>=1e7?7:e>=1e6?6:e>=1e5?5:e>=1e4?4:e>=1e3?3:e>=100?2:e>=10?1:0};dn.popCount=function(e){return e=e-(e>>>1&1431655765),e=(e&858993459)+(e>>>2&858993459),(e+(e>>>4)&252645135)*16843009>>>24};function Fb(e){var t=32;return e&=-e,e&&t--,e&65535&&(t-=16),e&16711935&&(t-=8),e&252645135&&(t-=4),e&858993459&&(t-=2),e&1431655765&&(t-=1),t}dn.countTrailingZeros=Fb;dn.nextPow2=function(e){return e+=e===0,--e,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e+1};dn.prevPow2=function(e){return e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e-(e>>>1)};dn.parity=function(e){return e^=e>>>16,e^=e>>>8,e^=e>>>4,e&=15,27030>>>e&1};var ll=new Array(256);(function(e){for(var t=0;t<256;++t){var r=t,n=t,i=7;for(r>>>=1;r;r>>>=1)n<<=1,n|=r&1,--i;e[t]=n<>>8&255]<<16|ll[e>>>16&255]<<8|ll[e>>>24&255]};dn.interleave2=function(e,t){return e&=65535,e=(e|e<<8)&16711935,e=(e|e<<4)&252645135,e=(e|e<<2)&858993459,e=(e|e<<1)&1431655765,t&=65535,t=(t|t<<8)&16711935,t=(t|t<<4)&252645135,t=(t|t<<2)&858993459,t=(t|t<<1)&1431655765,e|t<<1};dn.deinterleave2=function(e,t){return e=e>>>t&1431655765,e=(e|e>>>1)&858993459,e=(e|e>>>2)&252645135,e=(e|e>>>4)&16711935,e=(e|e>>>16)&65535,e<<16>>16};dn.interleave3=function(e,t,r){return e&=1023,e=(e|e<<16)&4278190335,e=(e|e<<8)&251719695,e=(e|e<<4)&3272356035,e=(e|e<<2)&1227133513,t&=1023,t=(t|t<<16)&4278190335,t=(t|t<<8)&251719695,t=(t|t<<4)&3272356035,t=(t|t<<2)&1227133513,e|=t<<1,r&=1023,r=(r|r<<16)&4278190335,r=(r|r<<8)&251719695,r=(r|r<<4)&3272356035,r=(r|r<<2)&1227133513,e|r<<2};dn.deinterleave3=function(e,t){return e=e>>>t&1227133513,e=(e|e>>>2)&3272356035,e=(e|e>>>4)&251719695,e=(e|e>>>8)&4278190335,e=(e|e>>>16)&1023,e<<22>>22};dn.nextCombination=function(e){var t=e|e-1;return t+1|(~t&-~t)-1>>>Fb(e)+1}});var Ob=wn((lW,Ab)=>{\"use strict\";function Ib(e,t,r){var n=e[r]|0;if(n<=0)return[];var i=new Array(n),a;if(r===e.length-1)for(a=0;a0)return tO(e|0,t);break;case\"object\":if(typeof e.length==\"number\")return Ib(e,t,0);break}return[]}Ab.exports=nO});var eD={};Rf(eD,{Buffer:()=>fe,INSPECT_MAX_BYTES:()=>$b,SlowBuffer:()=>mO,isBuffer:()=>Qb,kMaxLength:()=>uO});function Hb(){Oh=!0;for(var e=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",t=0,r=e.length;t0)throw new Error(\"Invalid string. Length must be a multiple of 4\");a=e[s-2]===\"=\"?2:e[s-1]===\"=\"?1:0,o=new rO(s*3/4-a),n=a>0?s-4:s;var u=0;for(t=0,r=0;t>16&255,o[u++]=i>>8&255,o[u++]=i&255;return a===2?(i=Wr[e.charCodeAt(t)]<<2|Wr[e.charCodeAt(t+1)]>>4,o[u++]=i&255):a===1&&(i=Wr[e.charCodeAt(t)]<<10|Wr[e.charCodeAt(t+1)]<<4|Wr[e.charCodeAt(t+2)]>>2,o[u++]=i>>8&255,o[u++]=i&255),o}function aO(e){return wi[e>>18&63]+wi[e>>12&63]+wi[e>>6&63]+wi[e&63]}function oO(e,t,r){for(var n,i=[],a=t;au?u:s+o));return n===1?(t=e[r-1],i+=wi[t>>2],i+=wi[t<<4&63],i+=\"==\"):n===2&&(t=(e[r-2]<<8)+e[r-1],i+=wi[t>>10],i+=wi[t>>4&63],i+=wi[t<<2&63],i+=\"=\"),a.push(i),a.join(\"\")}function Ku(e,t,r,n,i){var a,o,s=i*8-n-1,u=(1<>1,c=-7,d=r?i-1:0,m=r?-1:1,h=e[t+d];for(d+=m,a=h&(1<<-c)-1,h>>=-c,c+=s;c>0;a=a*256+e[t+d],d+=m,c-=8);for(o=a&(1<<-c)-1,a>>=-c,c+=n;c>0;o=o*256+e[t+d],d+=m,c-=8);if(a===0)a=1-l;else{if(a===u)return o?NaN:(h?-1:1)*(1/0);o=o+Math.pow(2,n),a=a-l}return(h?-1:1)*o*Math.pow(2,a-n)}function Bb(e,t,r,n,i,a){var o,s,u,l=a*8-i-1,c=(1<>1,m=i===23?Math.pow(2,-24)-Math.pow(2,-77):0,h=n?0:a-1,g=n?1:-1,y=t<0||t===0&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,o=c):(o=Math.floor(Math.log(t)/Math.LN2),t*(u=Math.pow(2,-o))<1&&(o--,u*=2),o+d>=1?t+=m/u:t+=m*Math.pow(2,1-d),t*u>=2&&(o++,u/=2),o+d>=c?(s=0,o=c):o+d>=1?(s=(t*u-1)*Math.pow(2,i),o=o+d):(s=t*Math.pow(2,d-1)*Math.pow(2,i),o=0));i>=8;e[r+h]=s&255,h+=g,s/=256,i-=8);for(o=o<0;e[r+h]=o&255,h+=g,o/=256,l-=8);e[r+h-g]|=y*128}function Yu(){return fe.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function Wi(e,t){if(Yu()=Yu())throw new RangeError(\"Attempt to allocate Buffer larger than maximum size: 0x\"+Yu().toString(16)+\" bytes\");return e|0}function mO(e){return+e!=e&&(e=0),fe.alloc(+e)}function bi(e){return!!(e!=null&&e._isBuffer)}function Yb(e,t){if(bi(e))return e.length;if(typeof ArrayBuffer!=\"undefined\"&&typeof ArrayBuffer.isView==\"function\"&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;typeof e!=\"string\"&&(e=\"\"+e);var r=e.length;if(r===0)return 0;for(var n=!1;;)switch(t){case\"ascii\":case\"latin1\":case\"binary\":return r;case\"utf8\":case\"utf-8\":case void 0:return zu(e).length;case\"ucs2\":case\"ucs-2\":case\"utf16le\":case\"utf-16le\":return r*2;case\"hex\":return r>>>1;case\"base64\":return Zb(e).length;default:if(n)return zu(e).length;t=(\"\"+t).toLowerCase(),n=!0}}function pO(e,t,r){var n=!1;if((t===void 0||t<0)&&(t=0),t>this.length||((r===void 0||r>this.length)&&(r=this.length),r<=0)||(r>>>=0,t>>>=0,r<=t))return\"\";for(e||(e=\"utf8\");;)switch(e){case\"hex\":return xO(this,t,r);case\"utf8\":case\"utf-8\":return jb(this,t,r);case\"ascii\":return EO(this,t,r);case\"latin1\":case\"binary\":return kO(this,t,r);case\"base64\":return DO(this,t,r);case\"ucs2\":case\"ucs-2\":case\"utf16le\":case\"utf-16le\":return CO(this,t,r);default:if(n)throw new TypeError(\"Unknown encoding: \"+e);e=(e+\"\").toLowerCase(),n=!0}}function Ga(e,t,r){var n=e[t];e[t]=e[r],e[r]=n}function zb(e,t,r,n,i){if(e.length===0)return-1;if(typeof r==\"string\"?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),r=+r,isNaN(r)&&(r=i?0:e.length-1),r<0&&(r=e.length+r),r>=e.length){if(i)return-1;r=e.length-1}else if(r<0)if(i)r=0;else return-1;if(typeof t==\"string\"&&(t=fe.from(t,n)),bi(t))return t.length===0?-1:Nb(e,t,r,n,i);if(typeof t==\"number\")return t=t&255,fe.TYPED_ARRAY_SUPPORT&&typeof Uint8Array.prototype.indexOf==\"function\"?i?Uint8Array.prototype.indexOf.call(e,t,r):Uint8Array.prototype.lastIndexOf.call(e,t,r):Nb(e,[t],r,n,i);throw new TypeError(\"val must be string, number or Buffer\")}function Nb(e,t,r,n,i){var a=1,o=e.length,s=t.length;if(n!==void 0&&(n=String(n).toLowerCase(),n===\"ucs2\"||n===\"ucs-2\"||n===\"utf16le\"||n===\"utf-16le\")){if(e.length<2||t.length<2)return-1;a=2,o/=2,s/=2,r/=2}function u(h,g){return a===1?h[g]:h.readUInt16BE(g*a)}var l;if(i){var c=-1;for(l=r;lo&&(r=o-s),l=r;l>=0;l--){for(var d=!0,m=0;mi&&(n=i)):n=i;var a=t.length;if(a%2!==0)throw new TypeError(\"Invalid hex string\");n>a/2&&(n=a/2);for(var o=0;o239?4:a>223?3:a>191?2:1;if(i+s<=r){var u,l,c,d;switch(s){case 1:a<128&&(o=a);break;case 2:u=e[i+1],(u&192)===128&&(d=(a&31)<<6|u&63,d>127&&(o=d));break;case 3:u=e[i+1],l=e[i+2],(u&192)===128&&(l&192)===128&&(d=(a&15)<<12|(u&63)<<6|l&63,d>2047&&(d<55296||d>57343)&&(o=d));break;case 4:u=e[i+1],l=e[i+2],c=e[i+3],(u&192)===128&&(l&192)===128&&(c&192)===128&&(d=(a&15)<<18|(u&63)<<12|(l&63)<<6|c&63,d>65535&&d<1114112&&(o=d))}}o===null?(o=65533,s=1):o>65535&&(o-=65536,n.push(o>>>10&1023|55296),o=56320|o&1023),n.push(o),i+=s}return SO(n)}function SO(e){var t=e.length;if(t<=Rb)return String.fromCharCode.apply(String,e);for(var r=\"\",n=0;nn)&&(r=n);for(var i=\"\",a=t;ar)throw new RangeError(\"Trying to access beyond buffer length\")}function gr(e,t,r,n,i,a){if(!bi(e))throw new TypeError('\"buffer\" argument must be a Buffer instance');if(t>i||te.length)throw new RangeError(\"Index out of range\")}function ju(e,t,r,n){t<0&&(t=65535+t+1);for(var i=0,a=Math.min(e.length-r,2);i>>(n?i:1-i)*8}function qu(e,t,r,n){t<0&&(t=4294967295+t+1);for(var i=0,a=Math.min(e.length-r,4);i>>(n?i:3-i)*8&255}function qb(e,t,r,n,i,a){if(r+n>e.length)throw new RangeError(\"Index out of range\");if(r<0)throw new RangeError(\"Index out of range\")}function Gb(e,t,r,n,i){return i||qb(e,t,r,4),Bb(e,t,r,n,23,4),r+4}function Jb(e,t,r,n,i){return i||qb(e,t,r,8),Bb(e,t,r,n,52,8),r+8}function MO(e){if(e=TO(e).replace(_O,\"\"),e.length<2)return\"\";for(;e.length%4!==0;)e=e+\"=\";return e}function TO(e){return e.trim?e.trim():e.replace(/^\\s+|\\s+$/g,\"\")}function FO(e){return e<16?\"0\"+e.toString(16):e.toString(16)}function zu(e,t){t=t||1/0;for(var r,n=e.length,i=null,a=[],o=0;o55295&&r<57344){if(!i){if(r>56319){(t-=3)>-1&&a.push(239,191,189);continue}else if(o+1===n){(t-=3)>-1&&a.push(239,191,189);continue}i=r;continue}if(r<56320){(t-=3)>-1&&a.push(239,191,189),i=r;continue}r=(i-55296<<10|r-56320)+65536}else i&&(t-=3)>-1&&a.push(239,191,189);if(i=null,r<128){if((t-=1)<0)break;a.push(r)}else if(r<2048){if((t-=2)<0)break;a.push(r>>6|192,r&63|128)}else if(r<65536){if((t-=3)<0)break;a.push(r>>12|224,r>>6&63|128,r&63|128)}else if(r<1114112){if((t-=4)<0)break;a.push(r>>18|240,r>>12&63|128,r>>6&63|128,r&63|128)}else throw new Error(\"Invalid code point\")}return a}function IO(e){for(var t=[],r=0;r>8,i=r%256,a.push(i),a.push(n);return a}function Zb(e){return iO(MO(e))}function Gu(e,t,r,n){for(var i=0;i=t.length||i>=e.length);++i)t[i+r]=e[i];return i}function OO(e){return e!==e}function Qb(e){return e!=null&&(!!e._isBuffer||Xb(e)||LO(e))}function Xb(e){return!!e.constructor&&typeof e.constructor.isBuffer==\"function\"&&e.constructor.isBuffer(e)}function LO(e){return typeof e.readFloatLE==\"function\"&&typeof e.slice==\"function\"&&Xb(e.slice(0,0))}var wi,Wr,rO,Oh,sO,Vb,$b,Pb,lO,uO,Rb,_O,tD=hF(()=>{wi=[],Wr=[],rO=typeof Uint8Array!=\"undefined\"?Uint8Array:Array,Oh=!1;sO={}.toString,Vb=Array.isArray||function(e){return sO.call(e)==\"[object Array]\"};$b=50,Pb=window;fe.TYPED_ARRAY_SUPPORT=Pb.TYPED_ARRAY_SUPPORT!==void 0?Pb.TYPED_ARRAY_SUPPORT:!0;lO=Yu(),uO=lO;fe.poolSize=8192;fe._augment=function(e){return e.__proto__=fe.prototype,e};fe.from=function(e,t,r){return Ub(null,e,t,r)};fe.TYPED_ARRAY_SUPPORT&&(fe.prototype.__proto__=Uint8Array.prototype,fe.__proto__=Uint8Array);fe.alloc=function(e,t,r){return cO(null,e,t,r)};fe.allocUnsafe=function(e){return Lh(null,e)};fe.allocUnsafeSlow=function(e){return Lh(null,e)};fe.isBuffer=Qb;fe.compare=function(t,r){if(!bi(t)||!bi(r))throw new TypeError(\"Arguments must be Buffers\");if(t===r)return 0;for(var n=t.length,i=r.length,a=0,o=Math.min(n,i);a0&&(t=this.toString(\"hex\",0,r).match(/.{2}/g).join(\" \"),this.length>r&&(t+=\" ... \")),\"\"};fe.prototype.compare=function(t,r,n,i,a){if(!bi(t))throw new TypeError(\"Argument must be a Buffer\");if(r===void 0&&(r=0),n===void 0&&(n=t?t.length:0),i===void 0&&(i=0),a===void 0&&(a=this.length),r<0||n>t.length||i<0||a>this.length)throw new RangeError(\"out of range index\");if(i>=a&&r>=n)return 0;if(i>=a)return-1;if(r>=n)return 1;if(r>>>=0,n>>>=0,i>>>=0,a>>>=0,this===t)return 0;for(var o=a-i,s=n-r,u=Math.min(o,s),l=this.slice(i,a),c=t.slice(r,n),d=0;da)&&(n=a),t.length>0&&(n<0||r<0)||r>this.length)throw new RangeError(\"Attempt to write outside buffer bounds\");i||(i=\"utf8\");for(var o=!1;;)switch(i){case\"hex\":return gO(this,t,r,n);case\"utf8\":case\"utf-8\":return yO(this,t,r,n);case\"ascii\":return Kb(this,t,r,n);case\"latin1\":case\"binary\":return vO(this,t,r,n);case\"base64\":return wO(this,t,r,n);case\"ucs2\":case\"ucs-2\":case\"utf16le\":case\"utf-16le\":return bO(this,t,r,n);default:if(o)throw new TypeError(\"Unknown encoding: \"+i);i=(\"\"+i).toLowerCase(),o=!0}};fe.prototype.toJSON=function(){return{type:\"Buffer\",data:Array.prototype.slice.call(this._arr||this,0)}};Rb=4096;fe.prototype.slice=function(t,r){var n=this.length;t=~~t,r=r===void 0?n:~~r,t<0?(t+=n,t<0&&(t=0)):t>n&&(t=n),r<0?(r+=n,r<0&&(r=0)):r>n&&(r=n),r0&&(a*=256);)i+=this[t+--r]*a;return i};fe.prototype.readUInt8=function(t,r){return r||Pn(t,1,this.length),this[t]};fe.prototype.readUInt16LE=function(t,r){return r||Pn(t,2,this.length),this[t]|this[t+1]<<8};fe.prototype.readUInt16BE=function(t,r){return r||Pn(t,2,this.length),this[t]<<8|this[t+1]};fe.prototype.readUInt32LE=function(t,r){return r||Pn(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2]<<16)+this[t+3]*16777216};fe.prototype.readUInt32BE=function(t,r){return r||Pn(t,4,this.length),this[t]*16777216+(this[t+1]<<16|this[t+2]<<8|this[t+3])};fe.prototype.readIntLE=function(t,r,n){t=t|0,r=r|0,n||Pn(t,r,this.length);for(var i=this[t],a=1,o=0;++o=a&&(i-=Math.pow(2,8*r)),i};fe.prototype.readIntBE=function(t,r,n){t=t|0,r=r|0,n||Pn(t,r,this.length);for(var i=r,a=1,o=this[t+--i];i>0&&(a*=256);)o+=this[t+--i]*a;return a*=128,o>=a&&(o-=Math.pow(2,8*r)),o};fe.prototype.readInt8=function(t,r){return r||Pn(t,1,this.length),this[t]&128?(255-this[t]+1)*-1:this[t]};fe.prototype.readInt16LE=function(t,r){r||Pn(t,2,this.length);var n=this[t]|this[t+1]<<8;return n&32768?n|4294901760:n};fe.prototype.readInt16BE=function(t,r){r||Pn(t,2,this.length);var n=this[t+1]|this[t]<<8;return n&32768?n|4294901760:n};fe.prototype.readInt32LE=function(t,r){return r||Pn(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24};fe.prototype.readInt32BE=function(t,r){return r||Pn(t,4,this.length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]};fe.prototype.readFloatLE=function(t,r){return r||Pn(t,4,this.length),Ku(this,t,!0,23,4)};fe.prototype.readFloatBE=function(t,r){return r||Pn(t,4,this.length),Ku(this,t,!1,23,4)};fe.prototype.readDoubleLE=function(t,r){return r||Pn(t,8,this.length),Ku(this,t,!0,52,8)};fe.prototype.readDoubleBE=function(t,r){return r||Pn(t,8,this.length),Ku(this,t,!1,52,8)};fe.prototype.writeUIntLE=function(t,r,n,i){if(t=+t,r=r|0,n=n|0,!i){var a=Math.pow(2,8*n)-1;gr(this,t,r,n,a,0)}var o=1,s=0;for(this[r]=t&255;++s=0&&(s*=256);)this[r+o]=t/s&255;return r+n};fe.prototype.writeUInt8=function(t,r,n){return t=+t,r=r|0,n||gr(this,t,r,1,255,0),fe.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),this[r]=t&255,r+1};fe.prototype.writeUInt16LE=function(t,r,n){return t=+t,r=r|0,n||gr(this,t,r,2,65535,0),fe.TYPED_ARRAY_SUPPORT?(this[r]=t&255,this[r+1]=t>>>8):ju(this,t,r,!0),r+2};fe.prototype.writeUInt16BE=function(t,r,n){return t=+t,r=r|0,n||gr(this,t,r,2,65535,0),fe.TYPED_ARRAY_SUPPORT?(this[r]=t>>>8,this[r+1]=t&255):ju(this,t,r,!1),r+2};fe.prototype.writeUInt32LE=function(t,r,n){return t=+t,r=r|0,n||gr(this,t,r,4,4294967295,0),fe.TYPED_ARRAY_SUPPORT?(this[r+3]=t>>>24,this[r+2]=t>>>16,this[r+1]=t>>>8,this[r]=t&255):qu(this,t,r,!0),r+4};fe.prototype.writeUInt32BE=function(t,r,n){return t=+t,r=r|0,n||gr(this,t,r,4,4294967295,0),fe.TYPED_ARRAY_SUPPORT?(this[r]=t>>>24,this[r+1]=t>>>16,this[r+2]=t>>>8,this[r+3]=t&255):qu(this,t,r,!1),r+4};fe.prototype.writeIntLE=function(t,r,n,i){if(t=+t,r=r|0,!i){var a=Math.pow(2,8*n-1);gr(this,t,r,n,a-1,-a)}var o=0,s=1,u=0;for(this[r]=t&255;++o>0)-u&255;return r+n};fe.prototype.writeIntBE=function(t,r,n,i){if(t=+t,r=r|0,!i){var a=Math.pow(2,8*n-1);gr(this,t,r,n,a-1,-a)}var o=n-1,s=1,u=0;for(this[r+o]=t&255;--o>=0&&(s*=256);)t<0&&u===0&&this[r+o+1]!==0&&(u=1),this[r+o]=(t/s>>0)-u&255;return r+n};fe.prototype.writeInt8=function(t,r,n){return t=+t,r=r|0,n||gr(this,t,r,1,127,-128),fe.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),t<0&&(t=255+t+1),this[r]=t&255,r+1};fe.prototype.writeInt16LE=function(t,r,n){return t=+t,r=r|0,n||gr(this,t,r,2,32767,-32768),fe.TYPED_ARRAY_SUPPORT?(this[r]=t&255,this[r+1]=t>>>8):ju(this,t,r,!0),r+2};fe.prototype.writeInt16BE=function(t,r,n){return t=+t,r=r|0,n||gr(this,t,r,2,32767,-32768),fe.TYPED_ARRAY_SUPPORT?(this[r]=t>>>8,this[r+1]=t&255):ju(this,t,r,!1),r+2};fe.prototype.writeInt32LE=function(t,r,n){return t=+t,r=r|0,n||gr(this,t,r,4,2147483647,-2147483648),fe.TYPED_ARRAY_SUPPORT?(this[r]=t&255,this[r+1]=t>>>8,this[r+2]=t>>>16,this[r+3]=t>>>24):qu(this,t,r,!0),r+4};fe.prototype.writeInt32BE=function(t,r,n){return t=+t,r=r|0,n||gr(this,t,r,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),fe.TYPED_ARRAY_SUPPORT?(this[r]=t>>>24,this[r+1]=t>>>16,this[r+2]=t>>>8,this[r+3]=t&255):qu(this,t,r,!1),r+4};fe.prototype.writeFloatLE=function(t,r,n){return Gb(this,t,r,!0,n)};fe.prototype.writeFloatBE=function(t,r,n){return Gb(this,t,r,!1,n)};fe.prototype.writeDoubleLE=function(t,r,n){return Jb(this,t,r,!0,n)};fe.prototype.writeDoubleBE=function(t,r,n){return Jb(this,t,r,!1,n)};fe.prototype.copy=function(t,r,n,i){if(n||(n=0),!i&&i!==0&&(i=this.length),r>=t.length&&(r=t.length),r||(r=0),i>0&&i=this.length)throw new RangeError(\"sourceStart out of bounds\");if(i<0)throw new RangeError(\"sourceEnd out of bounds\");i>this.length&&(i=this.length),t.length-r=0;--o)t[o+r]=this[o+n];else if(a<1e3||!fe.TYPED_ARRAY_SUPPORT)for(o=0;o>>0,n=n===void 0?this.length:n>>>0,t||(t=0);var o;if(typeof t==\"number\")for(o=r;o{var Ja=(tD(),ov(eD));if(Ja&&Ja.default){Ju.exports=Ja.default;for(let e in Ja)Ju.exports[e]=Ja[e]}else Ja&&(Ju.exports=Ja)});var Xu=wn(bt=>{\"use strict\";var pa=Wu(),Wn=Ob(),rD=nD().Buffer;window.__TYPEDARRAY_POOL||(window.__TYPEDARRAY_POOL={UINT8:Wn([32,0]),UINT16:Wn([32,0]),UINT32:Wn([32,0]),BIGUINT64:Wn([32,0]),INT8:Wn([32,0]),INT16:Wn([32,0]),INT32:Wn([32,0]),BIGINT64:Wn([32,0]),FLOAT:Wn([32,0]),DOUBLE:Wn([32,0]),DATA:Wn([32,0]),UINT8C:Wn([32,0]),BUFFER:Wn([32,0])});var PO=typeof Uint8ClampedArray!=\"undefined\",NO=typeof BigUint64Array!=\"undefined\",RO=typeof BigInt64Array!=\"undefined\",Sn=window.__TYPEDARRAY_POOL;Sn.UINT8C||(Sn.UINT8C=Wn([32,0]));Sn.BIGUINT64||(Sn.BIGUINT64=Wn([32,0]));Sn.BIGINT64||(Sn.BIGINT64=Wn([32,0]));Sn.BUFFER||(Sn.BUFFER=Wn([32,0]));var Zu=Sn.DATA,Qu=Sn.BUFFER;bt.free=function(t){if(rD.isBuffer(t))Qu[pa.log2(t.length)].push(t);else{if(Object.prototype.toString.call(t)!==\"[object ArrayBuffer]\"&&(t=t.buffer),!t)return;var r=t.length||t.byteLength,n=pa.log2(r)|0;Zu[n].push(t)}};function iD(e){if(e){var t=e.length||e.byteLength,r=pa.log2(t);Zu[r].push(e)}}function HO(e){iD(e.buffer)}bt.freeUint8=bt.freeUint16=bt.freeUint32=bt.freeBigUint64=bt.freeInt8=bt.freeInt16=bt.freeInt32=bt.freeBigInt64=bt.freeFloat32=bt.freeFloat=bt.freeFloat64=bt.freeDouble=bt.freeUint8Clamped=bt.freeDataView=HO;bt.freeArrayBuffer=iD;bt.freeBuffer=function(t){Qu[pa.log2(t.length)].push(t)};bt.malloc=function(t,r){if(r===void 0||r===\"arraybuffer\")return yr(t);switch(r){case\"uint8\":return Nh(t);case\"uint16\":return aD(t);case\"uint32\":return oD(t);case\"int8\":return sD(t);case\"int16\":return lD(t);case\"int32\":return uD(t);case\"float\":case\"float32\":return cD(t);case\"double\":case\"float64\":return dD(t);case\"uint8_clamped\":return fD(t);case\"bigint64\":return mD(t);case\"biguint64\":return hD(t);case\"buffer\":return gD(t);case\"data\":case\"dataview\":return pD(t);default:return null}return null};function yr(t){var t=pa.nextPow2(t),r=pa.log2(t),n=Zu[r];return n.length>0?n.pop():new ArrayBuffer(t)}bt.mallocArrayBuffer=yr;function Nh(e){return new Uint8Array(yr(e),0,e)}bt.mallocUint8=Nh;function aD(e){return new Uint16Array(yr(2*e),0,e)}bt.mallocUint16=aD;function oD(e){return new Uint32Array(yr(4*e),0,e)}bt.mallocUint32=oD;function sD(e){return new Int8Array(yr(e),0,e)}bt.mallocInt8=sD;function lD(e){return new Int16Array(yr(2*e),0,e)}bt.mallocInt16=lD;function uD(e){return new Int32Array(yr(4*e),0,e)}bt.mallocInt32=uD;function cD(e){return new Float32Array(yr(4*e),0,e)}bt.mallocFloat32=bt.mallocFloat=cD;function dD(e){return new Float64Array(yr(8*e),0,e)}bt.mallocFloat64=bt.mallocDouble=dD;function fD(e){return PO?new Uint8ClampedArray(yr(e),0,e):Nh(e)}bt.mallocUint8Clamped=fD;function hD(e){return NO?new BigUint64Array(yr(8*e),0,e):null}bt.mallocBigUint64=hD;function mD(e){return RO?new BigInt64Array(yr(8*e),0,e):null}bt.mallocBigInt64=mD;function pD(e){return new DataView(yr(e),0,e)}bt.mallocDataView=pD;function gD(e){e=pa.nextPow2(e);var t=pa.log2(e),r=Qu[t];return r.length>0?r.pop():new rD(e)}bt.mallocBuffer=gD;bt.clearCache=function(){for(var t=0;t<32;++t)Sn.UINT8[t].length=0,Sn.UINT16[t].length=0,Sn.UINT32[t].length=0,Sn.INT8[t].length=0,Sn.INT16[t].length=0,Sn.INT32[t].length=0,Sn.FLOAT[t].length=0,Sn.DOUBLE[t].length=0,Sn.BIGUINT64[t].length=0,Sn.BIGINT64[t].length=0,Sn.UINT8C[t].length=0,Zu[t].length=0,Qu[t].length=0}});var DD=wn((dW,bD)=>{\"use strict\";bD.exports=BO;var tc=32;function BO(e,t){t<=4*tc?nc(0,t-1,e):rc(0,t-1,e)}function nc(e,t,r){for(var n=2*(e+1),i=e+1;i<=t;++i){for(var a=r[n++],o=r[n++],s=i,u=n-2;s-- >e;){var l=r[u-2],c=r[u-1];if(lr[t+1]:!0}function ec(e,t,r,n){e*=2;var i=n[e];return i>1,s=o-n,u=o+n,l=i,c=s,d=o,m=u,h=a,g=e+1,y=t-1,v=0;Yi(l,c,r)&&(v=l,l=c,c=v),Yi(m,h,r)&&(v=m,m=h,h=v),Yi(l,d,r)&&(v=l,l=d,d=v),Yi(c,d,r)&&(v=c,c=d,d=v),Yi(l,m,r)&&(v=l,l=m,m=v),Yi(d,m,r)&&(v=d,d=m,m=v),Yi(c,h,r)&&(v=c,c=h,h=v),Yi(c,d,r)&&(v=c,c=d,d=v),Yi(m,h,r)&&(v=m,m=h,h=v);for(var D=r[2*c],I=r[2*c+1],C=r[2*m],x=r[2*m+1],O=2*l,A=2*d,P=2*h,B=2*i,G=2*o,J=2*a,Q=0;Q<2;++Q){var oe=r[O+Q],te=r[A+Q],re=r[P+Q];r[B+Q]=oe,r[G+Q]=te,r[J+Q]=re}vD(s,e,r),vD(u,t,r);for(var ne=g;ne<=y;++ne)if(ec(ne,D,I,r))ne!==g&&yD(ne,g,r),++g;else if(!ec(ne,C,x,r))for(;;)if(ec(y,C,x,r)){ec(y,D,I,r)?(VO(ne,g,y,r),++g,--y):(yD(ne,y,r),--y);break}else{if(--y{\"use strict\";SD.exports={init:UO,sweepBipartite:WO,sweepComplete:YO,scanBipartite:zO,scanComplete:KO};var En=Xu(),$O=Wu(),ic=DD(),Fr=1<<28,Qa=1024,Yn=En.mallocInt32(Qa),zi=En.mallocInt32(Qa),Ki=En.mallocInt32(Qa),Za=En.mallocInt32(Qa),Uo=En.mallocInt32(Qa),ul=En.mallocInt32(Qa),dt=En.mallocDouble(Qa*8);function UO(e){var t=$O.nextPow2(e);Yn.length>>1;ic(dt,I);for(var C=0,x=0,g=0;g=Fr)O=O-Fr|0,Wo(Ki,Za,x--,O);else if(O>=0)Wo(Yn,zi,C--,O);else if(O<=-Fr){O=-O-Fr|0;for(var A=0;A>>1;ic(dt,I);for(var C=0,x=0,O=0,g=0;g>1===dt[2*g+3]>>1&&(P=2,g+=1),A<0){for(var B=-(A>>1)-1,G=0;G>1)-1;P===0?Wo(Yn,zi,C--,B):P===1?Wo(Ki,Za,x--,B):P===2&&Wo(Uo,ul,O--,B)}}}function zO(e,t,r,n,i,a,o,s,u,l,c,d){var m=0,h=2*e,g=t,y=t+e,v=1,D=1;n?D=Fr:v=Fr;for(var I=i;I>>1;ic(dt,A);for(var P=0,I=0;I=Fr?(G=!n,C-=Fr):(G=!!n,C-=1),G)Yo(Yn,zi,P++,C);else{var J=d[C],Q=h*C,oe=c[Q+t+1],te=c[Q+t+1+e];e:for(var re=0;re>>1;ic(dt,C);for(var x=0,y=0;y=Fr)Yn[x++]=v-Fr;else{v-=1;var A=c[v],P=m*v,B=l[P+t+1],G=l[P+t+1+e];e:for(var J=0;J=0;--J)if(Yn[J]===v){for(var re=J+1;re{\"use strict\";var Xa=\"d\",jo=\"ax\",ED=\"vv\",Hh=\"fp\",cl=\"es\",ac=\"rs\",Uh=\"re\",dl=\"rb\",kD=\"ri\",zo=\"rp\",oc=\"bs\",Wh=\"be\",fl=\"bb\",xD=\"bi\",Ko=\"bp\",Bh=\"rv\",Vh=\"Q\",$h=[Xa,jo,ED,ac,Uh,dl,kD,oc,Wh,fl,xD];function jO(e,t,r){var n=\"bruteForce\"+(e?\"Red\":\"Blue\")+(t?\"Flip\":\"\")+(r?\"Full\":\"\"),i=[\"function \",n,\"(\",$h.join(),\"){\",\"var \",cl,\"=2*\",Xa,\";\"],a=\"for(var i=\"+ac+\",\"+zo+\"=\"+cl+\"*\"+ac+\";i<\"+Uh+\";++i,\"+zo+\"+=\"+cl+\"){var x0=\"+dl+\"[\"+jo+\"+\"+zo+\"],x1=\"+dl+\"[\"+jo+\"+\"+zo+\"+\"+Xa+\"],xi=\"+kD+\"[i];\",o=\"for(var j=\"+oc+\",\"+Ko+\"=\"+cl+\"*\"+oc+\";j<\"+Wh+\";++j,\"+Ko+\"+=\"+cl+\"){var y0=\"+fl+\"[\"+jo+\"+\"+Ko+\"],\"+(r?\"y1=\"+fl+\"[\"+jo+\"+\"+Ko+\"+\"+Xa+\"],\":\"\")+\"yi=\"+xD+\"[j];\";return e?i.push(a,Vh,\":\",o):i.push(o,Vh,\":\",a),r?i.push(\"if(y1\"+Wh+\"-\"+oc+\"){\"),e?(a(!0,!1),i.push(\"}else{\"),a(!1,!1)):(i.push(\"if(\"+Hh+\"){\"),a(!0,!0),i.push(\"}else{\"),a(!0,!1),i.push(\"}}else{if(\"+Hh+\"){\"),a(!1,!0),i.push(\"}else{\"),a(!1,!1),i.push(\"}\")),i.push(\"}}return \"+t);var o=r.join(\"\")+i.join(\"\"),s=new Function(o);return s()}Yh.partial=CD(!1);Yh.full=CD(!0)});var zh=wn((mW,MD)=>{\"use strict\";MD.exports=GO;var qO=\"for(var j=2*a,k=j*c,l=k,m=c,n=b,o=a+b,p=c;d>p;++p,k+=j){var _;if($)if(m===p)m+=1,l+=j;else{for(var s=0;j>s;++s){var t=e[k+s];e[k+s]=e[l],e[l++]=t}var u=f[p];f[p]=f[m],f[m++]=u}}return m\";function GO(e,t){var r=\"abcdef\".split(\"\").concat(t),n=[];return e.indexOf(\"lo\")>=0&&n.push(\"lo=e[k+n]\"),e.indexOf(\"hi\")>=0&&n.push(\"hi=e[k+o]\"),r.push(qO.replace(\"_\",n.join()).replace(\"$\",e)),Function.apply(void 0,r)}});var ID=wn((pW,FD)=>{\"use strict\";FD.exports=XO;var JO=zh(),TD=JO(\"lor&&i[d+t]>l;--c,d-=o){for(var m=d,h=d+o,g=0;g>>1,l=2*e,c=u,d=i[l*u+t];o=v?(c=y,d=v):g>=I?(c=h,d=g):(c=D,d=I):v>=I?(c=y,d=v):I>=g?(c=h,d=g):(c=D,d=I);for(var O=l*(s-1),A=l*c,C=0;C{\"use strict\";RD.exports=fL;var qo=Xu(),Kh=Wu(),PD=_D(),eL=PD.partial,tL=PD.full,ga=Rh(),nL=ID(),Go=zh(),AD=128,rL=1<<22,iL=1<<22,aL=Go(\"!(lo>=p0)&&!(p1>=hi)\",[\"p0\",\"p1\"]),OD=Go(\"lo===p0\",[\"p0\"]),oL=Go(\"lo0;){l-=1;var m=l*jh,h=dr[m],g=dr[m+1],y=dr[m+2],v=dr[m+3],D=dr[m+4],I=dr[m+5],C=l*qh,x=eo[C],O=eo[C+1],A=I&1,P=!!(I&16),B=i,G=a,J=s,Q=u;if(A&&(B=s,G=u,J=i,Q=a),!(I&2&&(y=oL(e,h,g,y,B,G,O),g>=y))&&!(I&4&&(g=sL(e,h,g,y,B,G,x),g>=y))){var oe=y-g,te=D-v;if(P){if(e*oe*(oe+te){\"use strict\";$D.exports=yL;var ya=Xu(),sc=Rh(),hL=HD();function mL(e,t){for(var r=0;r>>1;if(!(o<=0)){var s,u=ya.mallocDouble(2*o*i),l=ya.mallocInt32(i);if(i=BD(e,o,u,l),i>0){if(o===1&&n)sc.init(i),s=sc.sweepComplete(o,r,0,i,u,l,0,i,u,l);else{var c=ya.mallocDouble(2*o*a),d=ya.mallocInt32(a);a=BD(t,o,c,d),a>0&&(sc.init(i+a),o===1?s=sc.sweepBipartite(o,r,0,i,u,l,0,a,c,d):s=hL(o,r,n,i,u,l,a,c,d),ya.free(c),ya.free(d))}ya.free(u),ya.free(l)}return s}}}var hl;function VD(e,t){hl.push([e,t])}function pL(e){return hl=[],lc(e,e,VD,!0),hl}function gL(e,t){return hl=[],lc(e,t,VD,!1),hl}function yL(e,t,r){var n;switch(arguments.length){case 1:return pL(e);case 2:return typeof t==\"function\"?lc(e,e,t,!0):gL(e,t);case 3:return lc(e,t,r,!1);default:throw new Error(\"box-intersect: Invalid arguments\")}}});var o0=wn(gt=>{\"use strict\";Object.defineProperty(gt,\"__esModule\",{value:!0});var on=require(\"obsidian\"),Zh=\"YYYY-MM-DD\",Qh=\"gggg-[W]ww\",jD=\"YYYY-MM\",qD=\"YYYY-[Q]Q\",GD=\"YYYY\";function pl(e){var r,n;let t=window.app.plugins.getPlugin(\"periodic-notes\");return t&&((n=(r=t.settings)==null?void 0:r[e])==null?void 0:n.enabled)}function gl(){var e,t,r,n;try{let{internalPlugins:i,plugins:a}=window.app;if(pl(\"daily\")){let{format:l,folder:c,template:d}=((t=(e=a.getPlugin(\"periodic-notes\"))==null?void 0:e.settings)==null?void 0:t.daily)||{};return{format:l||Zh,folder:(c==null?void 0:c.trim())||\"\",template:(d==null?void 0:d.trim())||\"\"}}let{folder:o,format:s,template:u}=((n=(r=i.getPluginById(\"daily-notes\"))==null?void 0:r.instance)==null?void 0:n.options)||{};return{format:s||Zh,folder:(o==null?void 0:o.trim())||\"\",template:(u==null?void 0:u.trim())||\"\"}}catch(i){console.info(\"No custom daily note settings found!\",i)}}function yl(){var e,t,r,n,i,a,o;try{let s=window.app.plugins,u=(e=s.getPlugin(\"calendar\"))==null?void 0:e.options,l=(r=(t=s.getPlugin(\"periodic-notes\"))==null?void 0:t.settings)==null?void 0:r.weekly;if(pl(\"weekly\"))return{format:l.format||Qh,folder:((n=l.folder)==null?void 0:n.trim())||\"\",template:((i=l.template)==null?void 0:i.trim())||\"\"};let c=u||{};return{format:c.weeklyNoteFormat||Qh,folder:((a=c.weeklyNoteFolder)==null?void 0:a.trim())||\"\",template:((o=c.weeklyNoteTemplate)==null?void 0:o.trim())||\"\"}}catch(s){console.info(\"No custom weekly note settings found!\",s)}}function vl(){var t,r,n,i;let e=window.app.plugins;try{let a=pl(\"monthly\")&&((r=(t=e.getPlugin(\"periodic-notes\"))==null?void 0:t.settings)==null?void 0:r.monthly)||{};return{format:a.format||jD,folder:((n=a.folder)==null?void 0:n.trim())||\"\",template:((i=a.template)==null?void 0:i.trim())||\"\"}}catch(a){console.info(\"No custom monthly note settings found!\",a)}}function wl(){var t,r,n,i;let e=window.app.plugins;try{let a=pl(\"quarterly\")&&((r=(t=e.getPlugin(\"periodic-notes\"))==null?void 0:t.settings)==null?void 0:r.quarterly)||{};return{format:a.format||qD,folder:((n=a.folder)==null?void 0:n.trim())||\"\",template:((i=a.template)==null?void 0:i.trim())||\"\"}}catch(a){console.info(\"No custom quarterly note settings found!\",a)}}function bl(){var t,r,n,i;let e=window.app.plugins;try{let a=pl(\"yearly\")&&((r=(t=e.getPlugin(\"periodic-notes\"))==null?void 0:t.settings)==null?void 0:r.yearly)||{};return{format:a.format||GD,folder:((n=a.folder)==null?void 0:n.trim())||\"\",template:((i=a.template)==null?void 0:i.trim())||\"\"}}catch(a){console.info(\"No custom yearly note settings found!\",a)}}function JD(...e){let t=[];for(let n=0,i=e.length;n{let I=n(),C=e.clone().set({hour:I.get(\"hour\"),minute:I.get(\"minute\"),second:I.get(\"second\")});return g&&C.add(parseInt(y,10),v),D?C.format(D.substring(1).trim()):C.format(a)}).replace(/{{\\s*yesterday\\s*}}/gi,e.clone().subtract(1,\"day\").format(a)).replace(/{{\\s*tomorrow\\s*}}/gi,e.clone().add(1,\"d\").format(a)));return t.foldManager.save(d,u),d}catch(d){console.error(`Failed to create file: '${c}'`,d),new on.Notice(\"Unable to create new file.\")}}function AL(e,t){var r;return(r=t[ni(e,\"day\")])!=null?r:null}function OL(){let{vault:e}=window.app,{folder:t}=gl(),r=e.getAbstractFileByPath(on.normalizePath(t));if(!r)throw new Xh(\"Failed to find daily notes folder\");let n={};return on.Vault.recurseChildren(r,i=>{if(i instanceof on.TFile){let a=Zo(i,\"day\");if(a){let o=ni(a,\"day\");n[o]=i}}}),n}var em=class extends Error{};function LL(){let{moment:e}=window,t=e.localeData()._week.dow,r=[\"sunday\",\"monday\",\"tuesday\",\"wednesday\",\"thursday\",\"friday\",\"saturday\"];for(;t;)r.push(r.shift()),t--;return r}function PL(e){return LL().indexOf(e.toLowerCase())}async function e0(e){let{vault:t}=window.app,{template:r,format:n,folder:i}=yl(),[a,o]=await Jo(r),s=e.format(n),u=await Dl(i,s);try{let l=await t.create(u,a.replace(/{{\\s*(date|time)\\s*(([+-]\\d+)([yqmwdhs]))?\\s*(:.+?)?}}/gi,(c,d,m,h,g,y)=>{let v=window.moment(),D=e.clone().set({hour:v.get(\"hour\"),minute:v.get(\"minute\"),second:v.get(\"second\")});return m&&D.add(parseInt(h,10),g),y?D.format(y.substring(1).trim()):D.format(n)}).replace(/{{\\s*title\\s*}}/gi,s).replace(/{{\\s*time\\s*}}/gi,window.moment().format(\"HH:mm\")).replace(/{{\\s*(sunday|monday|tuesday|wednesday|thursday|friday|saturday)\\s*:(.*?)}}/gi,(c,d,m)=>{let h=PL(d);return e.weekday(h).format(m.trim())}));return window.app.foldManager.save(l,o),l}catch(l){console.error(`Failed to create file: '${u}'`,l),new on.Notice(\"Unable to create new file.\")}}function NL(e,t){var r;return(r=t[ni(e,\"week\")])!=null?r:null}function RL(){let e={};if(!n0())return e;let{vault:t}=window.app,{folder:r}=yl(),n=t.getAbstractFileByPath(on.normalizePath(r));if(!n)throw new em(\"Failed to find weekly notes folder\");return on.Vault.recurseChildren(n,i=>{if(i instanceof on.TFile){let a=Zo(i,\"week\");if(a){let o=ni(a,\"week\");e[o]=i}}}),e}var tm=class extends Error{};async function t0(e){let{vault:t}=window.app,{template:r,format:n,folder:i}=vl(),[a,o]=await Jo(r),s=e.format(n),u=await Dl(i,s);try{let l=await t.create(u,a.replace(/{{\\s*(date|time)\\s*(([+-]\\d+)([yqmwdhs]))?\\s*(:.+?)?}}/gi,(c,d,m,h,g,y)=>{let v=window.moment(),D=e.clone().set({hour:v.get(\"hour\"),minute:v.get(\"minute\"),second:v.get(\"second\")});return m&&D.add(parseInt(h,10),g),y?D.format(y.substring(1).trim()):D.format(n)}).replace(/{{\\s*date\\s*}}/gi,s).replace(/{{\\s*time\\s*}}/gi,window.moment().format(\"HH:mm\")).replace(/{{\\s*title\\s*}}/gi,s));return window.app.foldManager.save(l,o),l}catch(l){console.error(`Failed to create file: '${u}'`,l),new on.Notice(\"Unable to create new file.\")}}function HL(e,t){var r;return(r=t[ni(e,\"month\")])!=null?r:null}function BL(){let e={};if(!r0())return e;let{vault:t}=window.app,{folder:r}=vl(),n=t.getAbstractFileByPath(on.normalizePath(r));if(!n)throw new tm(\"Failed to find monthly notes folder\");return on.Vault.recurseChildren(n,i=>{if(i instanceof on.TFile){let a=Zo(i,\"month\");if(a){let o=ni(a,\"month\");e[o]=i}}}),e}var nm=class extends Error{};async function VL(e){let{vault:t}=window.app,{template:r,format:n,folder:i}=wl(),[a,o]=await Jo(r),s=e.format(n),u=await Dl(i,s);try{let l=await t.create(u,a.replace(/{{\\s*(date|time)\\s*(([+-]\\d+)([yqmwdhs]))?\\s*(:.+?)?}}/gi,(c,d,m,h,g,y)=>{let v=window.moment(),D=e.clone().set({hour:v.get(\"hour\"),minute:v.get(\"minute\"),second:v.get(\"second\")});return m&&D.add(parseInt(h,10),g),y?D.format(y.substring(1).trim()):D.format(n)}).replace(/{{\\s*date\\s*}}/gi,s).replace(/{{\\s*time\\s*}}/gi,window.moment().format(\"HH:mm\")).replace(/{{\\s*title\\s*}}/gi,s));return window.app.foldManager.save(l,o),l}catch(l){console.error(`Failed to create file: '${u}'`,l),new on.Notice(\"Unable to create new file.\")}}function $L(e,t){var r;return(r=t[ni(e,\"quarter\")])!=null?r:null}function UL(){let e={};if(!i0())return e;let{vault:t}=window.app,{folder:r}=wl(),n=t.getAbstractFileByPath(on.normalizePath(r));if(!n)throw new nm(\"Failed to find quarterly notes folder\");return on.Vault.recurseChildren(n,i=>{if(i instanceof on.TFile){let a=Zo(i,\"quarter\");if(a){let o=ni(a,\"quarter\");e[o]=i}}}),e}var rm=class extends Error{};async function WL(e){let{vault:t}=window.app,{template:r,format:n,folder:i}=bl(),[a,o]=await Jo(r),s=e.format(n),u=await Dl(i,s);try{let l=await t.create(u,a.replace(/{{\\s*(date|time)\\s*(([+-]\\d+)([yqmwdhs]))?\\s*(:.+?)?}}/gi,(c,d,m,h,g,y)=>{let v=window.moment(),D=e.clone().set({hour:v.get(\"hour\"),minute:v.get(\"minute\"),second:v.get(\"second\")});return m&&D.add(parseInt(h,10),g),y?D.format(y.substring(1).trim()):D.format(n)}).replace(/{{\\s*date\\s*}}/gi,s).replace(/{{\\s*time\\s*}}/gi,window.moment().format(\"HH:mm\")).replace(/{{\\s*title\\s*}}/gi,s));return window.app.foldManager.save(l,o),l}catch(l){console.error(`Failed to create file: '${u}'`,l),new on.Notice(\"Unable to create new file.\")}}function YL(e,t){var r;return(r=t[ni(e,\"year\")])!=null?r:null}function zL(){let e={};if(!a0())return e;let{vault:t}=window.app,{folder:r}=bl(),n=t.getAbstractFileByPath(on.normalizePath(r));if(!n)throw new rm(\"Failed to find yearly notes folder\");return on.Vault.recurseChildren(n,i=>{if(i instanceof on.TFile){let a=Zo(i,\"year\");if(a){let o=ni(a,\"year\");e[o]=i}}}),e}function KL(){var n,i;let{app:e}=window,t=e.internalPlugins.plugins[\"daily-notes\"];if(t&&t.enabled)return!0;let r=e.plugins.getPlugin(\"periodic-notes\");return r&&((i=(n=r.settings)==null?void 0:n.daily)==null?void 0:i.enabled)}function n0(){var r,n;let{app:e}=window;if(e.plugins.getPlugin(\"calendar\"))return!0;let t=e.plugins.getPlugin(\"periodic-notes\");return t&&((n=(r=t.settings)==null?void 0:r.weekly)==null?void 0:n.enabled)}function r0(){var r,n;let{app:e}=window,t=e.plugins.getPlugin(\"periodic-notes\");return t&&((n=(r=t.settings)==null?void 0:r.monthly)==null?void 0:n.enabled)}function i0(){var r,n;let{app:e}=window,t=e.plugins.getPlugin(\"periodic-notes\");return t&&((n=(r=t.settings)==null?void 0:r.quarterly)==null?void 0:n.enabled)}function a0(){var r,n;let{app:e}=window,t=e.plugins.getPlugin(\"periodic-notes\");return t&&((n=(r=t.settings)==null?void 0:r.yearly)==null?void 0:n.enabled)}function jL(e){let t={day:gl,week:yl,month:vl,quarter:wl,year:bl}[e];return t()}function qL(e,t){return{day:XD,month:t0,week:e0}[e](t)}gt.DEFAULT_DAILY_NOTE_FORMAT=Zh;gt.DEFAULT_MONTHLY_NOTE_FORMAT=jD;gt.DEFAULT_QUARTERLY_NOTE_FORMAT=qD;gt.DEFAULT_WEEKLY_NOTE_FORMAT=Qh;gt.DEFAULT_YEARLY_NOTE_FORMAT=GD;gt.appHasDailyNotesPluginLoaded=KL;gt.appHasMonthlyNotesPluginLoaded=r0;gt.appHasQuarterlyNotesPluginLoaded=i0;gt.appHasWeeklyNotesPluginLoaded=n0;gt.appHasYearlyNotesPluginLoaded=a0;gt.createDailyNote=XD;gt.createMonthlyNote=t0;gt.createPeriodicNote=qL;gt.createQuarterlyNote=VL;gt.createWeeklyNote=e0;gt.createYearlyNote=WL;gt.getAllDailyNotes=OL;gt.getAllMonthlyNotes=BL;gt.getAllQuarterlyNotes=UL;gt.getAllWeeklyNotes=RL;gt.getAllYearlyNotes=zL;gt.getDailyNote=AL;gt.getDailyNoteSettings=gl;gt.getDateFromFile=Zo;gt.getDateFromPath=IL;gt.getDateUID=ni;gt.getMonthlyNote=HL;gt.getMonthlyNoteSettings=vl;gt.getPeriodicNoteSettings=jL;gt.getQuarterlyNote=$L;gt.getQuarterlyNoteSettings=wl;gt.getTemplateInfo=Jo;gt.getWeeklyNote=NL;gt.getWeeklyNoteSettings=yl;gt.getYearlyNote=YL;gt.getYearlyNoteSettings=bl});var Ic=wn(Ei=>{\"use strict\";Object.defineProperty(Ei,\"__esModule\",{value:!0});require(\"obsidian\");var qi=class extends Error{},fm=class extends qi{constructor(t){super(`Invalid DateTime: ${t.toMessage()}`)}},hm=class extends qi{constructor(t){super(`Invalid Interval: ${t.toMessage()}`)}},mm=class extends qi{constructor(t){super(`Invalid Duration: ${t.toMessage()}`)}},ro=class extends qi{},yc=class extends qi{constructor(t){super(`Invalid unit ${t}`)}},vr=class extends qi{},Di=class extends qi{constructor(){super(\"Zone is an abstract class\")}},_e=\"numeric\",oi=\"short\",Or=\"long\",vc={year:_e,month:_e,day:_e},H0={year:_e,month:oi,day:_e},GL={year:_e,month:oi,day:_e,weekday:oi},B0={year:_e,month:Or,day:_e},V0={year:_e,month:Or,day:_e,weekday:Or},$0={hour:_e,minute:_e},U0={hour:_e,minute:_e,second:_e},W0={hour:_e,minute:_e,second:_e,timeZoneName:oi},Y0={hour:_e,minute:_e,second:_e,timeZoneName:Or},z0={hour:_e,minute:_e,hourCycle:\"h23\"},K0={hour:_e,minute:_e,second:_e,hourCycle:\"h23\"},j0={hour:_e,minute:_e,second:_e,hourCycle:\"h23\",timeZoneName:oi},q0={hour:_e,minute:_e,second:_e,hourCycle:\"h23\",timeZoneName:Or},G0={year:_e,month:_e,day:_e,hour:_e,minute:_e},J0={year:_e,month:_e,day:_e,hour:_e,minute:_e,second:_e},Z0={year:_e,month:oi,day:_e,hour:_e,minute:_e},Q0={year:_e,month:oi,day:_e,hour:_e,minute:_e,second:_e},JL={year:_e,month:oi,day:_e,weekday:oi,hour:_e,minute:_e},X0={year:_e,month:Or,day:_e,hour:_e,minute:_e,timeZoneName:oi},eS={year:_e,month:Or,day:_e,hour:_e,minute:_e,second:_e,timeZoneName:oi},tS={year:_e,month:Or,day:_e,weekday:Or,hour:_e,minute:_e,timeZoneName:Or},nS={year:_e,month:Or,day:_e,weekday:Or,hour:_e,minute:_e,second:_e,timeZoneName:Or},oo=class{get type(){throw new Di}get name(){throw new Di}get ianaName(){return this.name}get isUniversal(){throw new Di}offsetName(t,r){throw new Di}formatOffset(t,r){throw new Di}offset(t){throw new Di}equals(t){throw new Di}get isValid(){throw new Di}},im=null,wc=class e extends oo{static get instance(){return im===null&&(im=new e),im}get type(){return\"system\"}get name(){return new Intl.DateTimeFormat().resolvedOptions().timeZone}get isUniversal(){return!1}offsetName(t,{format:r,locale:n}){return iS(t,r,n)}formatOffset(t,r){return _l(this.offset(t),r)}offset(t){return-new Date(t).getTimezoneOffset()}equals(t){return t.type===\"system\"}get isValid(){return!0}},pc={};function ZL(e){return pc[e]||(pc[e]=new Intl.DateTimeFormat(\"en-US\",{hour12:!1,timeZone:e,year:\"numeric\",month:\"2-digit\",day:\"2-digit\",hour:\"2-digit\",minute:\"2-digit\",second:\"2-digit\",era:\"short\"})),pc[e]}var QL={year:0,month:1,day:2,era:3,hour:4,minute:5,second:6};function XL(e,t){let r=e.format(t).replace(/\\u200E/g,\"\"),n=/(\\d+)\\/(\\d+)\\/(\\d+) (AD|BC),? (\\d+):(\\d+):(\\d+)/.exec(r),[,i,a,o,s,u,l,c]=n;return[o,i,a,s,u,l,c]}function e2(e,t){let r=e.formatToParts(t),n=[];for(let i=0;i=0?g:1e3+g,(m-h)/(60*1e3)}equals(t){return t.type===\"iana\"&&t.name===this.name}get isValid(){return this.valid}},s0={};function t2(e,t={}){let r=JSON.stringify([e,t]),n=s0[r];return n||(n=new Intl.ListFormat(e,t),s0[r]=n),n}var pm={};function gm(e,t={}){let r=JSON.stringify([e,t]),n=pm[r];return n||(n=new Intl.DateTimeFormat(e,t),pm[r]=n),n}var ym={};function n2(e,t={}){let r=JSON.stringify([e,t]),n=ym[r];return n||(n=new Intl.NumberFormat(e,t),ym[r]=n),n}var vm={};function r2(e,t={}){let{base:r,...n}=t,i=JSON.stringify([e,n]),a=vm[i];return a||(a=new Intl.RelativeTimeFormat(e,t),vm[i]=a),a}var xl=null;function i2(){return xl||(xl=new Intl.DateTimeFormat().resolvedOptions().locale,xl)}function a2(e){let t=e.indexOf(\"-x-\");t!==-1&&(e=e.substring(0,t));let r=e.indexOf(\"-u-\");if(r===-1)return[e];{let n,i;try{n=gm(e).resolvedOptions(),i=e}catch(s){let u=e.substring(0,r);n=gm(u).resolvedOptions(),i=u}let{numberingSystem:a,calendar:o}=n;return[i,a,o]}}function o2(e,t,r){return(r||t)&&(e.includes(\"-u-\")||(e+=\"-u\"),r&&(e+=`-ca-${r}`),t&&(e+=`-nu-${t}`)),e}function s2(e){let t=[];for(let r=1;r<=12;r++){let n=mt.utc(2009,r,1);t.push(e(n))}return t}function l2(e){let t=[];for(let r=1;r<=7;r++){let n=mt.utc(2016,11,13+r);t.push(e(n))}return t}function cc(e,t,r,n){let i=e.listingMode();return i===\"error\"?null:i===\"en\"?r(t):n(t)}function u2(e){return e.numberingSystem&&e.numberingSystem!==\"latn\"?!1:e.numberingSystem===\"latn\"||!e.locale||e.locale.startsWith(\"en\")||new Intl.DateTimeFormat(e.intl).resolvedOptions().numberingSystem===\"latn\"}var wm=class{constructor(t,r,n){this.padTo=n.padTo||0,this.floor=n.floor||!1;let{padTo:i,floor:a,...o}=n;if(!r||Object.keys(o).length>0){let s={useGrouping:!1,...n};n.padTo>0&&(s.minimumIntegerDigits=n.padTo),this.inf=n2(t,s)}}format(t){if(this.inf){let r=this.floor?Math.floor(t):t;return this.inf.format(r)}else{let r=this.floor?Math.floor(t):Om(t,3);return Tn(r,this.padTo)}}},bm=class{constructor(t,r,n){this.opts=n,this.originalZone=void 0;let i;if(this.opts.timeZone)this.dt=t;else if(t.zone.type===\"fixed\"){let o=-1*(t.offset/60),s=o>=0?`Etc/GMT+${o}`:`Etc/GMT${o}`;t.offset!==0&&Da.create(s).valid?(i=s,this.dt=t):(i=\"UTC\",this.dt=t.offset===0?t:t.setZone(\"UTC\").plus({minutes:t.offset}),this.originalZone=t.zone)}else t.zone.type===\"system\"?this.dt=t:t.zone.type===\"iana\"?(this.dt=t,i=t.zone.name):(i=\"UTC\",this.dt=t.setZone(\"UTC\").plus({minutes:t.offset}),this.originalZone=t.zone);let a={...this.opts};a.timeZone=a.timeZone||i,this.dtf=gm(r,a)}format(){return this.originalZone?this.formatToParts().map(({value:t})=>t).join(\"\"):this.dtf.format(this.dt.toJSDate())}formatToParts(){let t=this.dtf.formatToParts(this.dt.toJSDate());return this.originalZone?t.map(r=>{if(r.type===\"timeZoneName\"){let n=this.originalZone.offsetName(this.dt.ts,{locale:this.dt.locale,format:this.opts.timeZoneName});return{...r,value:n}}else return r}):t}resolvedOptions(){return this.dtf.resolvedOptions()}},Dm=class{constructor(t,r,n){this.opts={style:\"long\",...n},!r&&rS()&&(this.rtf=r2(t,n))}format(t,r){return this.rtf?this.rtf.format(t,r):x2(r,t,this.opts.numeric,this.opts.style!==\"long\")}formatToParts(t,r){return this.rtf?this.rtf.formatToParts(t,r):[]}},mn=class e{static fromOpts(t){return e.create(t.locale,t.numberingSystem,t.outputCalendar,t.defaultToEN)}static create(t,r,n,i=!1){let a=t||hn.defaultLocale,o=a||(i?\"en-US\":i2()),s=r||hn.defaultNumberingSystem,u=n||hn.defaultOutputCalendar;return new e(o,s,u,a)}static resetCache(){xl=null,pm={},ym={},vm={}}static fromObject({locale:t,numberingSystem:r,outputCalendar:n}={}){return e.create(t,r,n)}constructor(t,r,n,i){let[a,o,s]=a2(t);this.locale=a,this.numberingSystem=r||o||null,this.outputCalendar=n||s||null,this.intl=o2(this.locale,this.numberingSystem,this.outputCalendar),this.weekdaysCache={format:{},standalone:{}},this.monthsCache={format:{},standalone:{}},this.meridiemCache=null,this.eraCache={},this.specifiedLocale=i,this.fastNumbersCached=null}get fastNumbers(){return this.fastNumbersCached==null&&(this.fastNumbersCached=u2(this)),this.fastNumbersCached}listingMode(){let t=this.isEnglish(),r=(this.numberingSystem===null||this.numberingSystem===\"latn\")&&(this.outputCalendar===null||this.outputCalendar===\"gregory\");return t&&r?\"en\":\"intl\"}clone(t){return!t||Object.getOwnPropertyNames(t).length===0?this:e.create(t.locale||this.specifiedLocale,t.numberingSystem||this.numberingSystem,t.outputCalendar||this.outputCalendar,t.defaultToEN||!1)}redefaultToEN(t={}){return this.clone({...t,defaultToEN:!0})}redefaultToSystem(t={}){return this.clone({...t,defaultToEN:!1})}months(t,r=!1){return cc(this,t,sS,()=>{let n=r?{month:t,day:\"numeric\"}:{month:t},i=r?\"format\":\"standalone\";return this.monthsCache[i][t]||(this.monthsCache[i][t]=s2(a=>this.extract(a,n,\"month\"))),this.monthsCache[i][t]})}weekdays(t,r=!1){return cc(this,t,cS,()=>{let n=r?{weekday:t,year:\"numeric\",month:\"long\",day:\"numeric\"}:{weekday:t},i=r?\"format\":\"standalone\";return this.weekdaysCache[i][t]||(this.weekdaysCache[i][t]=l2(a=>this.extract(a,n,\"weekday\"))),this.weekdaysCache[i][t]})}meridiems(){return cc(this,void 0,()=>dS,()=>{if(!this.meridiemCache){let t={hour:\"numeric\",hourCycle:\"h12\"};this.meridiemCache=[mt.utc(2016,11,13,9),mt.utc(2016,11,13,19)].map(r=>this.extract(r,t,\"dayperiod\"))}return this.meridiemCache})}eras(t){return cc(this,t,fS,()=>{let r={era:t};return this.eraCache[t]||(this.eraCache[t]=[mt.utc(-40,1,1),mt.utc(2017,1,1)].map(n=>this.extract(n,r,\"era\"))),this.eraCache[t]})}extract(t,r,n){let i=this.dtFormatter(t,r),a=i.formatToParts(),o=a.find(s=>s.type.toLowerCase()===n);return o?o.value:null}numberFormatter(t={}){return new wm(this.intl,t.forceSimple||this.fastNumbers,t)}dtFormatter(t,r={}){return new bm(t,this.intl,r)}relFormatter(t={}){return new Dm(this.intl,this.isEnglish(),t)}listFormatter(t={}){return t2(this.intl,t)}isEnglish(){return this.locale===\"en\"||this.locale.toLowerCase()===\"en-us\"||new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith(\"en-us\")}equals(t){return this.locale===t.locale&&this.numberingSystem===t.numberingSystem&&this.outputCalendar===t.outputCalendar}},am=null,Kr=class e extends oo{static get utcInstance(){return am===null&&(am=new e(0)),am}static instance(t){return t===0?e.utcInstance:new e(t)}static parseSpecifier(t){if(t){let r=t.match(/^utc(?:([+-]\\d{1,2})(?::(\\d{2}))?)?$/i);if(r)return new e(Tc(r[1],r[2]))}return null}constructor(t){super(),this.fixed=t}get type(){return\"fixed\"}get name(){return this.fixed===0?\"UTC\":`UTC${_l(this.fixed,\"narrow\")}`}get ianaName(){return this.fixed===0?\"Etc/UTC\":`Etc/GMT${_l(-this.fixed,\"narrow\")}`}offsetName(){return this.name}formatOffset(t,r){return _l(this.fixed,r)}get isUniversal(){return!0}offset(){return this.fixed}equals(t){return t.type===\"fixed\"&&t.fixed===this.fixed}get isValid(){return!0}},Sm=class extends oo{constructor(t){super(),this.zoneName=t}get type(){return\"invalid\"}get name(){return this.zoneName}get isUniversal(){return!1}offsetName(){return null}formatOffset(){return\"\"}offset(){return NaN}equals(){return!1}get isValid(){return!1}};function ba(e,t){if(kt(e)||e===null)return t;if(e instanceof oo)return e;if(c2(e)){let r=e.toLowerCase();return r===\"default\"?t:r===\"local\"||r===\"system\"?wc.instance:r===\"utc\"||r===\"gmt\"?Kr.utcInstance:Kr.parseSpecifier(r)||Da.create(e)}else return ao(e)?Kr.instance(e):typeof e==\"object\"&&\"offset\"in e&&typeof e.offset==\"function\"?e:new Sm(e)}var l0=()=>Date.now(),u0=\"system\",c0=null,d0=null,f0=null,h0=60,m0,hn=class{static get now(){return l0}static set now(t){l0=t}static set defaultZone(t){u0=t}static get defaultZone(){return ba(u0,wc.instance)}static get defaultLocale(){return c0}static set defaultLocale(t){c0=t}static get defaultNumberingSystem(){return d0}static set defaultNumberingSystem(t){d0=t}static get defaultOutputCalendar(){return f0}static set defaultOutputCalendar(t){f0=t}static get twoDigitCutoffYear(){return h0}static set twoDigitCutoffYear(t){h0=t%100}static get throwOnInvalid(){return m0}static set throwOnInvalid(t){m0=t}static resetCaches(){mn.resetCache(),Da.resetCache()}};function kt(e){return typeof e==\"undefined\"}function ao(e){return typeof e==\"number\"}function _c(e){return typeof e==\"number\"&&e%1===0}function c2(e){return typeof e==\"string\"}function d2(e){return Object.prototype.toString.call(e)===\"[object Date]\"}function rS(){try{return typeof Intl!=\"undefined\"&&!!Intl.RelativeTimeFormat}catch(e){return!1}}function f2(e){return Array.isArray(e)?e:[e]}function p0(e,t,r){if(e.length!==0)return e.reduce((n,i)=>{let a=[t(i),i];return n&&r(n[0],a[0])===n[0]?n:a},null)[1]}function h2(e,t){return t.reduce((r,n)=>(r[n]=e[n],r),{})}function rs(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function ji(e,t,r){return _c(e)&&e>=t&&e<=r}function m2(e,t){return e-t*Math.floor(e/t)}function Tn(e,t=2){let r=e<0,n;return r?n=\"-\"+(\"\"+-e).padStart(t,\"0\"):n=(\"\"+e).padStart(t,\"0\"),n}function wa(e){if(!(kt(e)||e===null||e===\"\"))return parseInt(e,10)}function to(e){if(!(kt(e)||e===null||e===\"\"))return parseFloat(e)}function Am(e){if(!(kt(e)||e===null||e===\"\")){let t=parseFloat(\"0.\"+e)*1e3;return Math.floor(t)}}function Om(e,t,r=!1){let n=10**t;return(r?Math.trunc:Math.round)(e*n)/n}function Fl(e){return e%4===0&&(e%100!==0||e%400===0)}function Cl(e){return Fl(e)?366:365}function bc(e,t){let r=m2(t-1,12)+1,n=e+(t-r)/12;return r===2?Fl(n)?29:28:[31,null,31,30,31,30,31,31,30,31,30,31][r-1]}function Mc(e){let t=Date.UTC(e.year,e.month-1,e.day,e.hour,e.minute,e.second,e.millisecond);return e.year<100&&e.year>=0&&(t=new Date(t),t.setUTCFullYear(e.year,e.month-1,e.day)),+t}function Dc(e){let t=(e+Math.floor(e/4)-Math.floor(e/100)+Math.floor(e/400))%7,r=e-1,n=(r+Math.floor(r/4)-Math.floor(r/100)+Math.floor(r/400))%7;return t===4||n===3?53:52}function Em(e){return e>99?e:e>hn.twoDigitCutoffYear?1900+e:2e3+e}function iS(e,t,r,n=null){let i=new Date(e),a={hourCycle:\"h23\",year:\"numeric\",month:\"2-digit\",day:\"2-digit\",hour:\"2-digit\",minute:\"2-digit\"};n&&(a.timeZone=n);let o={timeZoneName:t,...a},s=new Intl.DateTimeFormat(r,o).formatToParts(i).find(u=>u.type.toLowerCase()===\"timezonename\");return s?s.value:null}function Tc(e,t){let r=parseInt(e,10);Number.isNaN(r)&&(r=0);let n=parseInt(t,10)||0,i=r<0||Object.is(r,-0)?-n:n;return r*60+i}function aS(e){let t=Number(e);if(typeof e==\"boolean\"||e===\"\"||Number.isNaN(t))throw new vr(`Invalid unit value ${e}`);return t}function Sc(e,t){let r={};for(let n in e)if(rs(e,n)){let i=e[n];if(i==null)continue;r[t(n)]=aS(i)}return r}function _l(e,t){let r=Math.trunc(Math.abs(e/60)),n=Math.trunc(Math.abs(e%60)),i=e>=0?\"+\":\"-\";switch(t){case\"short\":return`${i}${Tn(r,2)}:${Tn(n,2)}`;case\"narrow\":return`${i}${r}${n>0?`:${n}`:\"\"}`;case\"techie\":return`${i}${Tn(r,2)}${Tn(n,2)}`;default:throw new RangeError(`Value format ${t} is out of range for property format`)}}function Fc(e){return h2(e,[\"hour\",\"minute\",\"second\",\"millisecond\"])}var p2=[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],oS=[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],g2=[\"J\",\"F\",\"M\",\"A\",\"M\",\"J\",\"J\",\"A\",\"S\",\"O\",\"N\",\"D\"];function sS(e){switch(e){case\"narrow\":return[...g2];case\"short\":return[...oS];case\"long\":return[...p2];case\"numeric\":return[\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"10\",\"11\",\"12\"];case\"2-digit\":return[\"01\",\"02\",\"03\",\"04\",\"05\",\"06\",\"07\",\"08\",\"09\",\"10\",\"11\",\"12\"];default:return null}}var lS=[\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\",\"Sunday\"],uS=[\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\",\"Sun\"],y2=[\"M\",\"T\",\"W\",\"T\",\"F\",\"S\",\"S\"];function cS(e){switch(e){case\"narrow\":return[...y2];case\"short\":return[...uS];case\"long\":return[...lS];case\"numeric\":return[\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\"];default:return null}}var dS=[\"AM\",\"PM\"],v2=[\"Before Christ\",\"Anno Domini\"],w2=[\"BC\",\"AD\"],b2=[\"B\",\"A\"];function fS(e){switch(e){case\"narrow\":return[...b2];case\"short\":return[...w2];case\"long\":return[...v2];default:return null}}function D2(e){return dS[e.hour<12?0:1]}function S2(e,t){return cS(t)[e.weekday-1]}function E2(e,t){return sS(t)[e.month-1]}function k2(e,t){return fS(t)[e.year<0?0:1]}function x2(e,t,r=\"always\",n=!1){let i={years:[\"year\",\"yr.\"],quarters:[\"quarter\",\"qtr.\"],months:[\"month\",\"mo.\"],weeks:[\"week\",\"wk.\"],days:[\"day\",\"day\",\"days\"],hours:[\"hour\",\"hr.\"],minutes:[\"minute\",\"min.\"],seconds:[\"second\",\"sec.\"]},a=[\"hours\",\"minutes\",\"seconds\"].indexOf(e)===-1;if(r===\"auto\"&&a){let d=e===\"days\";switch(t){case 1:return d?\"tomorrow\":`next ${i[e][0]}`;case-1:return d?\"yesterday\":`last ${i[e][0]}`;case 0:return d?\"today\":`this ${i[e][0]}`}}let o=Object.is(t,-0)||t<0,s=Math.abs(t),u=s===1,l=i[e],c=n?u?l[1]:l[2]||l[1]:u?i[e][0]:e;return o?`${s} ${c} ago`:`in ${s} ${c}`}function g0(e,t){let r=\"\";for(let n of e)n.literal?r+=n.val:r+=t(n.val);return r}var C2={D:vc,DD:H0,DDD:B0,DDDD:V0,t:$0,tt:U0,ttt:W0,tttt:Y0,T:z0,TT:K0,TTT:j0,TTTT:q0,f:G0,ff:Z0,fff:X0,ffff:tS,F:J0,FF:Q0,FFF:eS,FFFF:nS},Ir=class e{static create(t,r={}){return new e(t,r)}static parseFormat(t){let r=null,n=\"\",i=!1,a=[];for(let o=0;o0&&a.push({literal:i||/^\\s+$/.test(n),val:n}),r=null,n=\"\",i=!i):i||s===r?n+=s:(n.length>0&&a.push({literal:/^\\s+$/.test(n),val:n}),n=s,r=s)}return n.length>0&&a.push({literal:i||/^\\s+$/.test(n),val:n}),a}static macroTokenToFormatOpts(t){return C2[t]}constructor(t,r){this.opts=r,this.loc=t,this.systemLoc=null}formatWithSystemDefault(t,r){return this.systemLoc===null&&(this.systemLoc=this.loc.redefaultToSystem()),this.systemLoc.dtFormatter(t,{...this.opts,...r}).format()}dtFormatter(t,r={}){return this.loc.dtFormatter(t,{...this.opts,...r})}formatDateTime(t,r){return this.dtFormatter(t,r).format()}formatDateTimeParts(t,r){return this.dtFormatter(t,r).formatToParts()}formatInterval(t,r){return this.dtFormatter(t.start,r).dtf.formatRange(t.start.toJSDate(),t.end.toJSDate())}resolvedOptions(t,r){return this.dtFormatter(t,r).resolvedOptions()}num(t,r=0){if(this.opts.forceSimple)return Tn(t,r);let n={...this.opts};return r>0&&(n.padTo=r),this.loc.numberFormatter(n).format(t)}formatDateTimeFromString(t,r){let n=this.loc.listingMode()===\"en\",i=this.loc.outputCalendar&&this.loc.outputCalendar!==\"gregory\",a=(h,g)=>this.loc.extract(t,h,g),o=h=>t.isOffsetFixed&&t.offset===0&&h.allowZ?\"Z\":t.isValid?t.zone.formatOffset(t.ts,h.format):\"\",s=()=>n?D2(t):a({hour:\"numeric\",hourCycle:\"h12\"},\"dayperiod\"),u=(h,g)=>n?E2(t,h):a(g?{month:h}:{month:h,day:\"numeric\"},\"month\"),l=(h,g)=>n?S2(t,h):a(g?{weekday:h}:{weekday:h,month:\"long\",day:\"numeric\"},\"weekday\"),c=h=>{let g=e.macroTokenToFormatOpts(h);return g?this.formatWithSystemDefault(t,g):h},d=h=>n?k2(t,h):a({era:h},\"era\"),m=h=>{switch(h){case\"S\":return this.num(t.millisecond);case\"u\":case\"SSS\":return this.num(t.millisecond,3);case\"s\":return this.num(t.second);case\"ss\":return this.num(t.second,2);case\"uu\":return this.num(Math.floor(t.millisecond/10),2);case\"uuu\":return this.num(Math.floor(t.millisecond/100));case\"m\":return this.num(t.minute);case\"mm\":return this.num(t.minute,2);case\"h\":return this.num(t.hour%12===0?12:t.hour%12);case\"hh\":return this.num(t.hour%12===0?12:t.hour%12,2);case\"H\":return this.num(t.hour);case\"HH\":return this.num(t.hour,2);case\"Z\":return o({format:\"narrow\",allowZ:this.opts.allowZ});case\"ZZ\":return o({format:\"short\",allowZ:this.opts.allowZ});case\"ZZZ\":return o({format:\"techie\",allowZ:this.opts.allowZ});case\"ZZZZ\":return t.zone.offsetName(t.ts,{format:\"short\",locale:this.loc.locale});case\"ZZZZZ\":return t.zone.offsetName(t.ts,{format:\"long\",locale:this.loc.locale});case\"z\":return t.zoneName;case\"a\":return s();case\"d\":return i?a({day:\"numeric\"},\"day\"):this.num(t.day);case\"dd\":return i?a({day:\"2-digit\"},\"day\"):this.num(t.day,2);case\"c\":return this.num(t.weekday);case\"ccc\":return l(\"short\",!0);case\"cccc\":return l(\"long\",!0);case\"ccccc\":return l(\"narrow\",!0);case\"E\":return this.num(t.weekday);case\"EEE\":return l(\"short\",!1);case\"EEEE\":return l(\"long\",!1);case\"EEEEE\":return l(\"narrow\",!1);case\"L\":return i?a({month:\"numeric\",day:\"numeric\"},\"month\"):this.num(t.month);case\"LL\":return i?a({month:\"2-digit\",day:\"numeric\"},\"month\"):this.num(t.month,2);case\"LLL\":return u(\"short\",!0);case\"LLLL\":return u(\"long\",!0);case\"LLLLL\":return u(\"narrow\",!0);case\"M\":return i?a({month:\"numeric\"},\"month\"):this.num(t.month);case\"MM\":return i?a({month:\"2-digit\"},\"month\"):this.num(t.month,2);case\"MMM\":return u(\"short\",!1);case\"MMMM\":return u(\"long\",!1);case\"MMMMM\":return u(\"narrow\",!1);case\"y\":return i?a({year:\"numeric\"},\"year\"):this.num(t.year);case\"yy\":return i?a({year:\"2-digit\"},\"year\"):this.num(t.year.toString().slice(-2),2);case\"yyyy\":return i?a({year:\"numeric\"},\"year\"):this.num(t.year,4);case\"yyyyyy\":return i?a({year:\"numeric\"},\"year\"):this.num(t.year,6);case\"G\":return d(\"short\");case\"GG\":return d(\"long\");case\"GGGGG\":return d(\"narrow\");case\"kk\":return this.num(t.weekYear.toString().slice(-2),2);case\"kkkk\":return this.num(t.weekYear,4);case\"W\":return this.num(t.weekNumber);case\"WW\":return this.num(t.weekNumber,2);case\"o\":return this.num(t.ordinal);case\"ooo\":return this.num(t.ordinal,3);case\"q\":return this.num(t.quarter);case\"qq\":return this.num(t.quarter,2);case\"X\":return this.num(Math.floor(t.ts/1e3));case\"x\":return this.num(t.ts);default:return c(h)}};return g0(e.parseFormat(r),m)}formatDurationFromString(t,r){let n=u=>{switch(u[0]){case\"S\":return\"millisecond\";case\"s\":return\"second\";case\"m\":return\"minute\";case\"h\":return\"hour\";case\"d\":return\"day\";case\"w\":return\"week\";case\"M\":return\"month\";case\"y\":return\"year\";default:return null}},i=u=>l=>{let c=n(l);return c?this.num(u.get(c),l.length):l},a=e.parseFormat(r),o=a.reduce((u,{literal:l,val:c})=>l?u:u.concat(c),[]),s=t.shiftTo(...o.map(n).filter(u=>u));return g0(a,i(s))}},Ar=class{constructor(t,r){this.reason=t,this.explanation=r}toMessage(){return this.explanation?`${this.reason}: ${this.explanation}`:this.reason}},hS=/[A-Za-z_+-]{1,256}(?::?\\/[A-Za-z0-9_+-]{1,256}(?:\\/[A-Za-z0-9_+-]{1,256})?)?/;function is(...e){let t=e.reduce((r,n)=>r+n.source,\"\");return RegExp(`^${t}$`)}function as(...e){return t=>e.reduce(([r,n,i],a)=>{let[o,s,u]=a(t,i);return[{...r,...o},s||n,u]},[{},null,1]).slice(0,2)}function os(e,...t){if(e==null)return[null,null];for(let[r,n]of t){let i=r.exec(e);if(i)return n(i)}return[null,null]}function mS(...e){return(t,r)=>{let n={},i;for(i=0;ih!==void 0&&(g||h&&c)?-h:h;return[{years:m(to(r)),months:m(to(n)),weeks:m(to(i)),days:m(to(a)),hours:m(to(o)),minutes:m(to(s)),seconds:m(to(u),u===\"-0\"),milliseconds:m(Am(l),d)}]}var B2={GMT:0,EDT:-4*60,EST:-5*60,CDT:-5*60,CST:-6*60,MDT:-6*60,MST:-7*60,PDT:-7*60,PST:-8*60};function Nm(e,t,r,n,i,a,o){let s={year:t.length===2?Em(wa(t)):wa(t),month:oS.indexOf(r)+1,day:wa(n),hour:wa(i),minute:wa(a)};return o&&(s.second=wa(o)),e&&(s.weekday=e.length>3?lS.indexOf(e)+1:uS.indexOf(e)+1),s}var V2=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s)?(\\d{1,2})\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s(\\d{2,4})\\s(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|(?:([+-]\\d\\d)(\\d\\d)))$/;function $2(e){let[,t,r,n,i,a,o,s,u,l,c,d]=e,m=Nm(t,i,n,r,a,o,s),h;return u?h=B2[u]:l?h=0:h=Tc(c,d),[m,new Kr(h)]}function U2(e){return e.replace(/\\([^()]*\\)|[\\n\\t]/g,\" \").replace(/(\\s\\s+)/g,\" \").trim()}var W2=/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\\d\\d) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\\d{4}) (\\d\\d):(\\d\\d):(\\d\\d) GMT$/,Y2=/^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\\d\\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\\d\\d) (\\d\\d):(\\d\\d):(\\d\\d) GMT$/,z2=/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( \\d|\\d\\d) (\\d\\d):(\\d\\d):(\\d\\d) (\\d{4})$/;function y0(e){let[,t,r,n,i,a,o,s]=e;return[Nm(t,i,n,r,a,o,s),Kr.utcInstance]}function K2(e){let[,t,r,n,i,a,o,s]=e;return[Nm(t,s,r,n,i,a,o),Kr.utcInstance]}var j2=is(M2,Pm),q2=is(T2,Pm),G2=is(F2,Pm),J2=is(gS),vS=as(P2,ss,Il,Al),Z2=as(I2,ss,Il,Al),Q2=as(A2,ss,Il,Al),X2=as(ss,Il,Al);function eP(e){return os(e,[j2,vS],[q2,Z2],[G2,Q2],[J2,X2])}function tP(e){return os(U2(e),[V2,$2])}function nP(e){return os(e,[W2,y0],[Y2,y0],[z2,K2])}function rP(e){return os(e,[R2,H2])}var iP=as(ss);function aP(e){return os(e,[N2,iP])}var oP=is(O2,L2),sP=is(yS),lP=as(ss,Il,Al);function uP(e){return os(e,[oP,vS],[sP,lP])}var v0=\"Invalid Duration\",wS={weeks:{days:7,hours:7*24,minutes:7*24*60,seconds:7*24*60*60,milliseconds:7*24*60*60*1e3},days:{hours:24,minutes:24*60,seconds:24*60*60,milliseconds:24*60*60*1e3},hours:{minutes:60,seconds:60*60,milliseconds:60*60*1e3},minutes:{seconds:60,milliseconds:60*1e3},seconds:{milliseconds:1e3}},cP={years:{quarters:4,months:12,weeks:52,days:365,hours:365*24,minutes:365*24*60,seconds:365*24*60*60,milliseconds:365*24*60*60*1e3},quarters:{months:3,weeks:13,days:91,hours:91*24,minutes:91*24*60,seconds:91*24*60*60,milliseconds:91*24*60*60*1e3},months:{weeks:4,days:30,hours:30*24,minutes:30*24*60,seconds:30*24*60*60,milliseconds:30*24*60*60*1e3},...wS},Yr=146097/400,Qo=146097/4800,dP={years:{quarters:4,months:12,weeks:Yr/7,days:Yr,hours:Yr*24,minutes:Yr*24*60,seconds:Yr*24*60*60,milliseconds:Yr*24*60*60*1e3},quarters:{months:3,weeks:Yr/28,days:Yr/4,hours:Yr*24/4,minutes:Yr*24*60/4,seconds:Yr*24*60*60/4,milliseconds:Yr*24*60*60*1e3/4},months:{weeks:Qo/7,days:Qo,hours:Qo*24,minutes:Qo*24*60,seconds:Qo*24*60*60,milliseconds:Qo*24*60*60*1e3},...wS},io=[\"years\",\"quarters\",\"months\",\"weeks\",\"days\",\"hours\",\"minutes\",\"seconds\",\"milliseconds\"],fP=io.slice(0).reverse();function va(e,t,r=!1){let n={values:r?t.values:{...e.values,...t.values||{}},loc:e.loc.clone(t.loc),conversionAccuracy:t.conversionAccuracy||e.conversionAccuracy,matrix:t.matrix||e.matrix};return new He(n)}function bS(e,t){var n;let r=(n=t.milliseconds)!=null?n:0;for(let i of fP.slice(1))t[i]&&(r+=t[i]*e[i].milliseconds);return r}function w0(e,t){let r=bS(e,t)<0?-1:1;io.reduceRight((n,i)=>{if(kt(t[i]))return n;if(n){let a=t[n]*r,o=e[i][n],s=Math.floor(a/o);t[i]+=s*r,t[n]-=s*o*r}return i},null),io.reduce((n,i)=>{if(kt(t[i]))return n;if(n){let a=t[n]%1;t[n]-=a,t[i]+=a*e[n][i]}return i},null)}function hP(e){let t={};for(let[r,n]of Object.entries(e))n!==0&&(t[r]=n);return t}var He=class e{constructor(t){let r=t.conversionAccuracy===\"longterm\"||!1,n=r?dP:cP;t.matrix&&(n=t.matrix),this.values=t.values,this.loc=t.loc||mn.create(),this.conversionAccuracy=r?\"longterm\":\"casual\",this.invalid=t.invalid||null,this.matrix=n,this.isLuxonDuration=!0}static fromMillis(t,r){return e.fromObject({milliseconds:t},r)}static fromObject(t,r={}){if(t==null||typeof t!=\"object\")throw new vr(`Duration.fromObject: argument expected to be an object, got ${t===null?\"null\":typeof t}`);return new e({values:Sc(t,e.normalizeUnit),loc:mn.fromObject(r),conversionAccuracy:r.conversionAccuracy,matrix:r.matrix})}static fromDurationLike(t){if(ao(t))return e.fromMillis(t);if(e.isDuration(t))return t;if(typeof t==\"object\")return e.fromObject(t);throw new vr(`Unknown duration argument ${t} of type ${typeof t}`)}static fromISO(t,r){let[n]=rP(t);return n?e.fromObject(n,r):e.invalid(\"unparsable\",`the input \"${t}\" can't be parsed as ISO 8601`)}static fromISOTime(t,r){let[n]=aP(t);return n?e.fromObject(n,r):e.invalid(\"unparsable\",`the input \"${t}\" can't be parsed as ISO 8601`)}static invalid(t,r=null){if(!t)throw new vr(\"need to specify a reason the Duration is invalid\");let n=t instanceof Ar?t:new Ar(t,r);if(hn.throwOnInvalid)throw new mm(n);return new e({invalid:n})}static normalizeUnit(t){let r={year:\"years\",years:\"years\",quarter:\"quarters\",quarters:\"quarters\",month:\"months\",months:\"months\",week:\"weeks\",weeks:\"weeks\",day:\"days\",days:\"days\",hour:\"hours\",hours:\"hours\",minute:\"minutes\",minutes:\"minutes\",second:\"seconds\",seconds:\"seconds\",millisecond:\"milliseconds\",milliseconds:\"milliseconds\"}[t&&t.toLowerCase()];if(!r)throw new yc(t);return r}static isDuration(t){return t&&t.isLuxonDuration||!1}get locale(){return this.isValid?this.loc.locale:null}get numberingSystem(){return this.isValid?this.loc.numberingSystem:null}toFormat(t,r={}){let n={...r,floor:r.round!==!1&&r.floor!==!1};return this.isValid?Ir.create(this.loc,n).formatDurationFromString(this,t):v0}toHuman(t={}){if(!this.isValid)return v0;let r=io.map(n=>{let i=this.values[n];return kt(i)?null:this.loc.numberFormatter({style:\"unit\",unitDisplay:\"long\",...t,unit:n.slice(0,-1)}).format(i)}).filter(n=>n);return this.loc.listFormatter({type:\"conjunction\",style:t.listStyle||\"narrow\",...t}).format(r)}toObject(){return this.isValid?{...this.values}:{}}toISO(){if(!this.isValid)return null;let t=\"P\";return this.years!==0&&(t+=this.years+\"Y\"),(this.months!==0||this.quarters!==0)&&(t+=this.months+this.quarters*3+\"M\"),this.weeks!==0&&(t+=this.weeks+\"W\"),this.days!==0&&(t+=this.days+\"D\"),(this.hours!==0||this.minutes!==0||this.seconds!==0||this.milliseconds!==0)&&(t+=\"T\"),this.hours!==0&&(t+=this.hours+\"H\"),this.minutes!==0&&(t+=this.minutes+\"M\"),(this.seconds!==0||this.milliseconds!==0)&&(t+=Om(this.seconds+this.milliseconds/1e3,3)+\"S\"),t===\"P\"&&(t+=\"T0S\"),t}toISOTime(t={}){if(!this.isValid)return null;let r=this.toMillis();return r<0||r>=864e5?null:(t={suppressMilliseconds:!1,suppressSeconds:!1,includePrefix:!1,format:\"extended\",...t,includeOffset:!1},mt.fromMillis(r,{zone:\"UTC\"}).toISOTime(t))}toJSON(){return this.toISO()}toString(){return this.toISO()}toMillis(){return this.isValid?bS(this.matrix,this.values):NaN}valueOf(){return this.toMillis()}plus(t){if(!this.isValid)return this;let r=e.fromDurationLike(t),n={};for(let i of io)(rs(r.values,i)||rs(this.values,i))&&(n[i]=r.get(i)+this.get(i));return va(this,{values:n},!0)}minus(t){if(!this.isValid)return this;let r=e.fromDurationLike(t);return this.plus(r.negate())}mapUnits(t){if(!this.isValid)return this;let r={};for(let n of Object.keys(this.values))r[n]=aS(t(this.values[n],n));return va(this,{values:r},!0)}get(t){return this[e.normalizeUnit(t)]}set(t){if(!this.isValid)return this;let r={...this.values,...Sc(t,e.normalizeUnit)};return va(this,{values:r})}reconfigure({locale:t,numberingSystem:r,conversionAccuracy:n,matrix:i}={}){let o={loc:this.loc.clone({locale:t,numberingSystem:r}),matrix:i,conversionAccuracy:n};return va(this,o)}as(t){return this.isValid?this.shiftTo(t).get(t):NaN}normalize(){if(!this.isValid)return this;let t=this.toObject();return w0(this.matrix,t),va(this,{values:t},!0)}rescale(){if(!this.isValid)return this;let t=hP(this.normalize().shiftToAll().toObject());return va(this,{values:t},!0)}shiftTo(...t){if(!this.isValid)return this;if(t.length===0)return this;t=t.map(o=>e.normalizeUnit(o));let r={},n={},i=this.toObject(),a;for(let o of io)if(t.indexOf(o)>=0){a=o;let s=0;for(let l in n)s+=this.matrix[l][o]*n[l],n[l]=0;ao(i[o])&&(s+=i[o]);let u=Math.trunc(s);r[o]=u,n[o]=(s*1e3-u*1e3)/1e3}else ao(i[o])&&(n[o]=i[o]);for(let o in n)n[o]!==0&&(r[a]+=o===a?n[o]:n[o]/this.matrix[a][o]);return w0(this.matrix,r),va(this,{values:r},!0)}shiftToAll(){return this.isValid?this.shiftTo(\"years\",\"months\",\"weeks\",\"days\",\"hours\",\"minutes\",\"seconds\",\"milliseconds\"):this}negate(){if(!this.isValid)return this;let t={};for(let r of Object.keys(this.values))t[r]=this.values[r]===0?0:-this.values[r];return va(this,{values:t},!0)}get years(){return this.isValid?this.values.years||0:NaN}get quarters(){return this.isValid?this.values.quarters||0:NaN}get months(){return this.isValid?this.values.months||0:NaN}get weeks(){return this.isValid?this.values.weeks||0:NaN}get days(){return this.isValid?this.values.days||0:NaN}get hours(){return this.isValid?this.values.hours||0:NaN}get minutes(){return this.isValid?this.values.minutes||0:NaN}get seconds(){return this.isValid?this.values.seconds||0:NaN}get milliseconds(){return this.isValid?this.values.milliseconds||0:NaN}get isValid(){return this.invalid===null}get invalidReason(){return this.invalid?this.invalid.reason:null}get invalidExplanation(){return this.invalid?this.invalid.explanation:null}equals(t){if(!this.isValid||!t.isValid||!this.loc.equals(t.loc))return!1;function r(n,i){return n===void 0||n===0?i===void 0||i===0:n===i}for(let n of io)if(!r(this.values[n],t.values[n]))return!1;return!0}},Xo=\"Invalid Interval\";function mP(e,t){return!e||!e.isValid?ns.invalid(\"missing or invalid start\"):!t||!t.isValid?ns.invalid(\"missing or invalid end\"):tt:!1}isBefore(t){return this.isValid?this.e<=t:!1}contains(t){return this.isValid?this.s<=t&&this.e>t:!1}set({start:t,end:r}={}){return this.isValid?e.fromDateTimes(t||this.s,r||this.e):this}splitAt(...t){if(!this.isValid)return[];let r=t.map(El).filter(o=>this.contains(o)).sort(),n=[],{s:i}=this,a=0;for(;i+this.e?this.e:o;n.push(e.fromDateTimes(i,s)),i=s,a+=1}return n}splitBy(t){let r=He.fromDurationLike(t);if(!this.isValid||!r.isValid||r.as(\"milliseconds\")===0)return[];let{s:n}=this,i=1,a,o=[];for(;nu*i));a=+s>+this.e?this.e:s,o.push(e.fromDateTimes(n,a)),n=a,i+=1}return o}divideEqually(t){return this.isValid?this.splitBy(this.length()/t).slice(0,t):[]}overlaps(t){return this.e>t.s&&this.s=t.e:!1}equals(t){return!this.isValid||!t.isValid?!1:this.s.equals(t.s)&&this.e.equals(t.e)}intersection(t){if(!this.isValid)return this;let r=this.s>t.s?this.s:t.s,n=this.e=n?null:e.fromDateTimes(r,n)}union(t){if(!this.isValid)return this;let r=this.st.e?this.e:t.e;return e.fromDateTimes(r,n)}static merge(t){let[r,n]=t.sort((i,a)=>i.s-a.s).reduce(([i,a],o)=>a?a.overlaps(o)||a.abutsStart(o)?[i,a.union(o)]:[i.concat([a]),o]:[i,o],[[],null]);return n&&r.push(n),r}static xor(t){let r=null,n=0,i=[],a=t.map(u=>[{time:u.s,type:\"s\"},{time:u.e,type:\"e\"}]),o=Array.prototype.concat(...a),s=o.sort((u,l)=>u.time-l.time);for(let u of s)n+=u.type===\"s\"?1:-1,n===1?r=u.time:(r&&+r!=+u.time&&i.push(e.fromDateTimes(r,u.time)),r=null);return e.merge(i)}difference(...t){return e.xor([this].concat(t)).map(r=>this.intersection(r)).filter(r=>r&&!r.isEmpty())}toString(){return this.isValid?`[${this.s.toISO()} \\u2013 ${this.e.toISO()})`:Xo}toLocaleString(t=vc,r={}){return this.isValid?Ir.create(this.s.loc.clone(r),t).formatInterval(this):Xo}toISO(t){return this.isValid?`${this.s.toISO(t)}/${this.e.toISO(t)}`:Xo}toISODate(){return this.isValid?`${this.s.toISODate()}/${this.e.toISODate()}`:Xo}toISOTime(t){return this.isValid?`${this.s.toISOTime(t)}/${this.e.toISOTime(t)}`:Xo}toFormat(t,{separator:r=\" \\u2013 \"}={}){return this.isValid?`${this.s.toFormat(t)}${r}${this.e.toFormat(t)}`:Xo}toDuration(t,r){return this.isValid?this.e.diff(this.s,t,r):He.invalid(this.invalidReason)}mapEndpoints(t){return e.fromDateTimes(t(this.s),t(this.e))}},es=class{static hasDST(t=hn.defaultZone){let r=mt.now().setZone(t).set({month:12});return!t.isUniversal&&r.offset!==r.set({month:6}).offset}static isValidIANAZone(t){return Da.isValidZone(t)}static normalizeZone(t){return ba(t,hn.defaultZone)}static months(t=\"long\",{locale:r=null,numberingSystem:n=null,locObj:i=null,outputCalendar:a=\"gregory\"}={}){return(i||mn.create(r,n,a)).months(t)}static monthsFormat(t=\"long\",{locale:r=null,numberingSystem:n=null,locObj:i=null,outputCalendar:a=\"gregory\"}={}){return(i||mn.create(r,n,a)).months(t,!0)}static weekdays(t=\"long\",{locale:r=null,numberingSystem:n=null,locObj:i=null}={}){return(i||mn.create(r,n,null)).weekdays(t)}static weekdaysFormat(t=\"long\",{locale:r=null,numberingSystem:n=null,locObj:i=null}={}){return(i||mn.create(r,n,null)).weekdays(t,!0)}static meridiems({locale:t=null}={}){return mn.create(t).meridiems()}static eras(t=\"short\",{locale:r=null}={}){return mn.create(r,null,\"gregory\").eras(t)}static features(){return{relative:rS()}}};function b0(e,t){let r=i=>i.toUTC(0,{keepLocalTime:!0}).startOf(\"day\").valueOf(),n=r(t)-r(e);return Math.floor(He.fromMillis(n).as(\"days\"))}function pP(e,t,r){let n=[[\"years\",(u,l)=>l.year-u.year],[\"quarters\",(u,l)=>l.quarter-u.quarter+(l.year-u.year)*4],[\"months\",(u,l)=>l.month-u.month+(l.year-u.year)*12],[\"weeks\",(u,l)=>{let c=b0(u,l);return(c-c%7)/7}],[\"days\",b0]],i={},a=e,o,s;for(let[u,l]of n)r.indexOf(u)>=0&&(o=u,i[u]=l(e,t),s=a.plus(i),s>t?(i[u]--,e=a.plus(i),e>t&&(s=e,i[u]--,e=a.plus(i))):e=s);return[e,i,s,o]}function gP(e,t,r,n){let[i,a,o,s]=pP(e,t,r),u=t-i,l=r.filter(d=>[\"hours\",\"minutes\",\"seconds\",\"milliseconds\"].indexOf(d)>=0);l.length===0&&(o0?He.fromMillis(u,n).shiftTo(...l).plus(c):c}var Rm={arab:\"[\\u0660-\\u0669]\",arabext:\"[\\u06F0-\\u06F9]\",bali:\"[\\u1B50-\\u1B59]\",beng:\"[\\u09E6-\\u09EF]\",deva:\"[\\u0966-\\u096F]\",fullwide:\"[\\uFF10-\\uFF19]\",gujr:\"[\\u0AE6-\\u0AEF]\",hanidec:\"[\\u3007|\\u4E00|\\u4E8C|\\u4E09|\\u56DB|\\u4E94|\\u516D|\\u4E03|\\u516B|\\u4E5D]\",khmr:\"[\\u17E0-\\u17E9]\",knda:\"[\\u0CE6-\\u0CEF]\",laoo:\"[\\u0ED0-\\u0ED9]\",limb:\"[\\u1946-\\u194F]\",mlym:\"[\\u0D66-\\u0D6F]\",mong:\"[\\u1810-\\u1819]\",mymr:\"[\\u1040-\\u1049]\",orya:\"[\\u0B66-\\u0B6F]\",tamldec:\"[\\u0BE6-\\u0BEF]\",telu:\"[\\u0C66-\\u0C6F]\",thai:\"[\\u0E50-\\u0E59]\",tibt:\"[\\u0F20-\\u0F29]\",latn:\"\\\\d\"},D0={arab:[1632,1641],arabext:[1776,1785],bali:[6992,7001],beng:[2534,2543],deva:[2406,2415],fullwide:[65296,65303],gujr:[2790,2799],khmr:[6112,6121],knda:[3302,3311],laoo:[3792,3801],limb:[6470,6479],mlym:[3430,3439],mong:[6160,6169],mymr:[4160,4169],orya:[2918,2927],tamldec:[3046,3055],telu:[3174,3183],thai:[3664,3673],tibt:[3872,3881]},yP=Rm.hanidec.replace(/[\\[|\\]]/g,\"\").split(\"\");function vP(e){let t=parseInt(e,10);if(isNaN(t)){t=\"\";for(let r=0;r=a&&n<=o&&(t+=n-a)}}return parseInt(t,10)}else return t}function ri({numberingSystem:e},t=\"\"){return new RegExp(`${Rm[e||\"latn\"]}${t}`)}var wP=\"missing Intl.DateTimeFormat.formatToParts support\";function Nt(e,t=r=>r){return{regex:e,deser:([r])=>t(vP(r))}}var bP=\"\\xA0\",DS=`[ ${bP}]`,SS=new RegExp(DS,\"g\");function DP(e){return e.replace(/\\./g,\"\\\\.?\").replace(SS,DS)}function S0(e){return e.replace(/\\./g,\"\").replace(SS,\" \").toLowerCase()}function ii(e,t){return e===null?null:{regex:RegExp(e.map(DP).join(\"|\")),deser:([r])=>e.findIndex(n=>S0(r)===S0(n))+t}}function E0(e,t){return{regex:e,deser:([,r,n])=>Tc(r,n),groups:t}}function dc(e){return{regex:e,deser:([t])=>t}}function SP(e){return e.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g,\"\\\\$&\")}function EP(e,t){let r=ri(t),n=ri(t,\"{2}\"),i=ri(t,\"{3}\"),a=ri(t,\"{4}\"),o=ri(t,\"{6}\"),s=ri(t,\"{1,2}\"),u=ri(t,\"{1,3}\"),l=ri(t,\"{1,6}\"),c=ri(t,\"{1,9}\"),d=ri(t,\"{2,4}\"),m=ri(t,\"{4,6}\"),h=v=>({regex:RegExp(SP(v.val)),deser:([D])=>D,literal:!0}),y=(v=>{if(e.literal)return h(v);switch(v.val){case\"G\":return ii(t.eras(\"short\"),0);case\"GG\":return ii(t.eras(\"long\"),0);case\"y\":return Nt(l);case\"yy\":return Nt(d,Em);case\"yyyy\":return Nt(a);case\"yyyyy\":return Nt(m);case\"yyyyyy\":return Nt(o);case\"M\":return Nt(s);case\"MM\":return Nt(n);case\"MMM\":return ii(t.months(\"short\",!0),1);case\"MMMM\":return ii(t.months(\"long\",!0),1);case\"L\":return Nt(s);case\"LL\":return Nt(n);case\"LLL\":return ii(t.months(\"short\",!1),1);case\"LLLL\":return ii(t.months(\"long\",!1),1);case\"d\":return Nt(s);case\"dd\":return Nt(n);case\"o\":return Nt(u);case\"ooo\":return Nt(i);case\"HH\":return Nt(n);case\"H\":return Nt(s);case\"hh\":return Nt(n);case\"h\":return Nt(s);case\"mm\":return Nt(n);case\"m\":return Nt(s);case\"q\":return Nt(s);case\"qq\":return Nt(n);case\"s\":return Nt(s);case\"ss\":return Nt(n);case\"S\":return Nt(u);case\"SSS\":return Nt(i);case\"u\":return dc(c);case\"uu\":return dc(s);case\"uuu\":return Nt(r);case\"a\":return ii(t.meridiems(),0);case\"kkkk\":return Nt(a);case\"kk\":return Nt(d,Em);case\"W\":return Nt(s);case\"WW\":return Nt(n);case\"E\":case\"c\":return Nt(r);case\"EEE\":return ii(t.weekdays(\"short\",!1),1);case\"EEEE\":return ii(t.weekdays(\"long\",!1),1);case\"ccc\":return ii(t.weekdays(\"short\",!0),1);case\"cccc\":return ii(t.weekdays(\"long\",!0),1);case\"Z\":case\"ZZ\":return E0(new RegExp(`([+-]${s.source})(?::(${n.source}))?`),2);case\"ZZZ\":return E0(new RegExp(`([+-]${s.source})(${n.source})?`),2);case\"z\":return dc(/[a-z_+-/]{1,256}?/i);case\" \":return dc(/[^\\S\\n\\r]/);default:return h(v)}})(e)||{invalidReason:wP};return y.token=e,y}var kP={year:{\"2-digit\":\"yy\",numeric:\"yyyyy\"},month:{numeric:\"M\",\"2-digit\":\"MM\",short:\"MMM\",long:\"MMMM\"},day:{numeric:\"d\",\"2-digit\":\"dd\"},weekday:{short:\"EEE\",long:\"EEEE\"},dayperiod:\"a\",dayPeriod:\"a\",hour12:{numeric:\"h\",\"2-digit\":\"hh\"},hour24:{numeric:\"H\",\"2-digit\":\"HH\"},minute:{numeric:\"m\",\"2-digit\":\"mm\"},second:{numeric:\"s\",\"2-digit\":\"ss\"},timeZoneName:{long:\"ZZZZZ\",short:\"ZZZ\"}};function xP(e,t,r){let{type:n,value:i}=e;if(n===\"literal\"){let u=/^\\s+$/.test(i);return{literal:!u,val:u?\" \":i}}let a=t[n],o=n;n===\"hour\"&&(t.hour12!=null?o=t.hour12?\"hour12\":\"hour24\":t.hourCycle!=null?t.hourCycle===\"h11\"||t.hourCycle===\"h12\"?o=\"hour12\":o=\"hour24\":o=r.hour12?\"hour12\":\"hour24\");let s=kP[o];if(typeof s==\"object\"&&(s=s[a]),s)return{literal:!1,val:s}}function CP(e){return[`^${e.map(r=>r.regex).reduce((r,n)=>`${r}(${n.source})`,\"\")}$`,e]}function _P(e,t,r){let n=e.match(t);if(n){let i={},a=1;for(let o in r)if(rs(r,o)){let s=r[o],u=s.groups?s.groups+1:1;!s.literal&&s.token&&(i[s.token.val[0]]=s.deser(n.slice(a,a+u))),a+=u}return[n,i]}else return[n,{}]}function MP(e){let t=a=>{switch(a){case\"S\":return\"millisecond\";case\"s\":return\"second\";case\"m\":return\"minute\";case\"h\":case\"H\":return\"hour\";case\"d\":return\"day\";case\"o\":return\"ordinal\";case\"L\":case\"M\":return\"month\";case\"y\":return\"year\";case\"E\":case\"c\":return\"weekday\";case\"W\":return\"weekNumber\";case\"k\":return\"weekYear\";case\"q\":return\"quarter\";default:return null}},r=null,n;return kt(e.z)||(r=Da.create(e.z)),kt(e.Z)||(r||(r=new Kr(e.Z)),n=e.Z),kt(e.q)||(e.M=(e.q-1)*3+1),kt(e.h)||(e.h<12&&e.a===1?e.h+=12:e.h===12&&e.a===0&&(e.h=0)),e.G===0&&e.y&&(e.y=-e.y),kt(e.u)||(e.S=Am(e.u)),[Object.keys(e).reduce((a,o)=>{let s=t(o);return s&&(a[s]=e[o]),a},{}),r,n]}var om=null;function TP(){return om||(om=mt.fromMillis(1555555555555)),om}function FP(e,t){if(e.literal)return e;let r=Ir.macroTokenToFormatOpts(e.val),n=xS(r,t);return n==null||n.includes(void 0)?e:n}function ES(e,t){return Array.prototype.concat(...e.map(r=>FP(r,t)))}function kS(e,t,r){let n=ES(Ir.parseFormat(r),e),i=n.map(o=>EP(o,e)),a=i.find(o=>o.invalidReason);if(a)return{input:t,tokens:n,invalidReason:a.invalidReason};{let[o,s]=CP(i),u=RegExp(o,\"i\"),[l,c]=_P(t,u,s),[d,m,h]=c?MP(c):[null,null,void 0];if(rs(c,\"a\")&&rs(c,\"H\"))throw new ro(\"Can't include meridiem when specifying 24-hour format\");return{input:t,tokens:n,regex:u,rawMatches:l,matches:c,result:d,zone:m,specificOffset:h}}}function IP(e,t,r){let{result:n,zone:i,specificOffset:a,invalidReason:o}=kS(e,t,r);return[n,i,a,o]}function xS(e,t){if(!e)return null;let n=Ir.create(t,e).dtFormatter(TP()),i=n.formatToParts(),a=n.resolvedOptions();return i.map(o=>xP(o,e,a))}var CS=[0,31,59,90,120,151,181,212,243,273,304,334],_S=[0,31,60,91,121,152,182,213,244,274,305,335];function zr(e,t){return new Ar(\"unit out of range\",`you specified ${t} (of type ${typeof t}) as a ${e}, which is invalid`)}function MS(e,t,r){let n=new Date(Date.UTC(e,t-1,r));e<100&&e>=0&&n.setUTCFullYear(n.getUTCFullYear()-1900);let i=n.getUTCDay();return i===0?7:i}function TS(e,t,r){return r+(Fl(e)?_S:CS)[t-1]}function FS(e,t){let r=Fl(e)?_S:CS,n=r.findIndex(a=>aDc(t)?(s=t+1,o=1):s=t,{weekYear:s,weekNumber:o,weekday:a,...Fc(e)}}function k0(e){let{weekYear:t,weekNumber:r,weekday:n}=e,i=MS(t,1,4),a=Cl(t),o=r*7+n-i-3,s;o<1?(s=t-1,o+=Cl(s)):o>a?(s=t+1,o-=Cl(t)):s=t;let{month:u,day:l}=FS(s,o);return{year:s,month:u,day:l,...Fc(e)}}function sm(e){let{year:t,month:r,day:n}=e,i=TS(t,r,n);return{year:t,ordinal:i,...Fc(e)}}function x0(e){let{year:t,ordinal:r}=e,{month:n,day:i}=FS(t,r);return{year:t,month:n,day:i,...Fc(e)}}function AP(e){let t=_c(e.weekYear),r=ji(e.weekNumber,1,Dc(e.weekYear)),n=ji(e.weekday,1,7);return t?r?n?!1:zr(\"weekday\",e.weekday):zr(\"week\",e.week):zr(\"weekYear\",e.weekYear)}function OP(e){let t=_c(e.year),r=ji(e.ordinal,1,Cl(e.year));return t?r?!1:zr(\"ordinal\",e.ordinal):zr(\"year\",e.year)}function IS(e){let t=_c(e.year),r=ji(e.month,1,12),n=ji(e.day,1,bc(e.year,e.month));return t?r?n?!1:zr(\"day\",e.day):zr(\"month\",e.month):zr(\"year\",e.year)}function AS(e){let{hour:t,minute:r,second:n,millisecond:i}=e,a=ji(t,0,23)||t===24&&r===0&&n===0&&i===0,o=ji(r,0,59),s=ji(n,0,59),u=ji(i,0,999);return a?o?s?u?!1:zr(\"millisecond\",i):zr(\"second\",n):zr(\"minute\",r):zr(\"hour\",t)}var lm=\"Invalid DateTime\",C0=864e13;function fc(e){return new Ar(\"unsupported zone\",`the zone \"${e.name}\" is not supported`)}function um(e){return e.weekData===null&&(e.weekData=km(e.c)),e.weekData}function no(e,t){let r={ts:e.ts,zone:e.zone,c:e.c,o:e.o,loc:e.loc,invalid:e.invalid};return new mt({...r,...t,old:r})}function OS(e,t,r){let n=e-t*60*1e3,i=r.offset(n);if(t===i)return[n,t];n-=(i-t)*60*1e3;let a=r.offset(n);return i===a?[n,i]:[e-Math.min(i,a)*60*1e3,Math.max(i,a)]}function hc(e,t){e+=t*60*1e3;let r=new Date(e);return{year:r.getUTCFullYear(),month:r.getUTCMonth()+1,day:r.getUTCDate(),hour:r.getUTCHours(),minute:r.getUTCMinutes(),second:r.getUTCSeconds(),millisecond:r.getUTCMilliseconds()}}function gc(e,t,r){return OS(Mc(e),t,r)}function _0(e,t){let r=e.o,n=e.c.year+Math.trunc(t.years),i=e.c.month+Math.trunc(t.months)+Math.trunc(t.quarters)*3,a={...e.c,year:n,month:i,day:Math.min(e.c.day,bc(n,i))+Math.trunc(t.days)+Math.trunc(t.weeks)*7},o=He.fromObject({years:t.years-Math.trunc(t.years),quarters:t.quarters-Math.trunc(t.quarters),months:t.months-Math.trunc(t.months),weeks:t.weeks-Math.trunc(t.weeks),days:t.days-Math.trunc(t.days),hours:t.hours,minutes:t.minutes,seconds:t.seconds,milliseconds:t.milliseconds}).as(\"milliseconds\"),s=Mc(a),[u,l]=OS(s,r,e.zone);return o!==0&&(u+=o,l=e.zone.offset(u)),{ts:u,o:l}}function Sl(e,t,r,n,i,a){let{setZone:o,zone:s}=r;if(e&&Object.keys(e).length!==0||t){let u=t||s,l=mt.fromObject(e,{...r,zone:u,specificOffset:a});return o?l:l.setZone(s)}else return mt.invalid(new Ar(\"unparsable\",`the input \"${i}\" can't be parsed as ${n}`))}function mc(e,t,r=!0){return e.isValid?Ir.create(mn.create(\"en-US\"),{allowZ:r,forceSimple:!0}).formatDateTimeFromString(e,t):null}function cm(e,t){let r=e.c.year>9999||e.c.year<0,n=\"\";return r&&e.c.year>=0&&(n+=\"+\"),n+=Tn(e.c.year,r?6:4),t?(n+=\"-\",n+=Tn(e.c.month),n+=\"-\",n+=Tn(e.c.day)):(n+=Tn(e.c.month),n+=Tn(e.c.day)),n}function M0(e,t,r,n,i,a){let o=Tn(e.c.hour);return t?(o+=\":\",o+=Tn(e.c.minute),(e.c.millisecond!==0||e.c.second!==0||!r)&&(o+=\":\")):o+=Tn(e.c.minute),(e.c.millisecond!==0||e.c.second!==0||!r)&&(o+=Tn(e.c.second),(e.c.millisecond!==0||!n)&&(o+=\".\",o+=Tn(e.c.millisecond,3))),i&&(e.isOffsetFixed&&e.offset===0&&!a?o+=\"Z\":e.o<0?(o+=\"-\",o+=Tn(Math.trunc(-e.o/60)),o+=\":\",o+=Tn(Math.trunc(-e.o%60))):(o+=\"+\",o+=Tn(Math.trunc(e.o/60)),o+=\":\",o+=Tn(Math.trunc(e.o%60)))),a&&(o+=\"[\"+e.zone.ianaName+\"]\"),o}var LS={month:1,day:1,hour:0,minute:0,second:0,millisecond:0},LP={weekNumber:1,weekday:1,hour:0,minute:0,second:0,millisecond:0},PP={ordinal:1,hour:0,minute:0,second:0,millisecond:0},PS=[\"year\",\"month\",\"day\",\"hour\",\"minute\",\"second\",\"millisecond\"],NP=[\"weekYear\",\"weekNumber\",\"weekday\",\"hour\",\"minute\",\"second\",\"millisecond\"],RP=[\"year\",\"ordinal\",\"hour\",\"minute\",\"second\",\"millisecond\"];function T0(e){let t={year:\"year\",years:\"year\",month:\"month\",months:\"month\",day:\"day\",days:\"day\",hour:\"hour\",hours:\"hour\",minute:\"minute\",minutes:\"minute\",quarter:\"quarter\",quarters:\"quarter\",second:\"second\",seconds:\"second\",millisecond:\"millisecond\",milliseconds:\"millisecond\",weekday:\"weekday\",weekdays:\"weekday\",weeknumber:\"weekNumber\",weeksnumber:\"weekNumber\",weeknumbers:\"weekNumber\",weekyear:\"weekYear\",weekyears:\"weekYear\",ordinal:\"ordinal\"}[e.toLowerCase()];if(!t)throw new yc(e);return t}function F0(e,t){let r=ba(t.zone,hn.defaultZone),n=mn.fromObject(t),i=hn.now(),a,o;if(kt(e.year))a=i;else{for(let l of PS)kt(e[l])&&(e[l]=LS[l]);let s=IS(e)||AS(e);if(s)return mt.invalid(s);let u=r.offset(i);[a,o]=gc(e,u,r)}return new mt({ts:a,zone:r,loc:n,o})}function I0(e,t,r){let n=kt(r.round)?!0:r.round,i=(o,s)=>(o=Om(o,n||r.calendary?0:2,!0),t.loc.clone(r).relFormatter(r).format(o,s)),a=o=>r.calendary?t.hasSame(e,o)?0:t.startOf(o).diff(e.startOf(o),o).get(o):t.diff(e,o).get(o);if(r.unit)return i(a(r.unit),r.unit);for(let o of r.units){let s=a(o);if(Math.abs(s)>=1)return i(s,o)}return i(e>t?-0:0,r.units[r.units.length-1])}function A0(e){let t={},r;return e.length>0&&typeof e[e.length-1]==\"object\"?(t=e[e.length-1],r=Array.from(e).slice(0,e.length-1)):r=Array.from(e),[t,r]}var mt=class e{constructor(t){let r=t.zone||hn.defaultZone,n=t.invalid||(Number.isNaN(t.ts)?new Ar(\"invalid input\"):null)||(r.isValid?null:fc(r));this.ts=kt(t.ts)?hn.now():t.ts;let i=null,a=null;if(!n)if(t.old&&t.old.ts===this.ts&&t.old.zone.equals(r))[i,a]=[t.old.c,t.old.o];else{let s=r.offset(this.ts);i=hc(this.ts,s),n=Number.isNaN(i.year)?new Ar(\"invalid input\"):null,i=n?null:i,a=n?null:s}this._zone=r,this.loc=t.loc||mn.create(),this.invalid=n,this.weekData=null,this.c=i,this.o=a,this.isLuxonDateTime=!0}static now(){return new e({})}static local(){let[t,r]=A0(arguments),[n,i,a,o,s,u,l]=r;return F0({year:n,month:i,day:a,hour:o,minute:s,second:u,millisecond:l},t)}static utc(){let[t,r]=A0(arguments),[n,i,a,o,s,u,l]=r;return t.zone=Kr.utcInstance,F0({year:n,month:i,day:a,hour:o,minute:s,second:u,millisecond:l},t)}static fromJSDate(t,r={}){let n=d2(t)?t.valueOf():NaN;if(Number.isNaN(n))return e.invalid(\"invalid input\");let i=ba(r.zone,hn.defaultZone);return i.isValid?new e({ts:n,zone:i,loc:mn.fromObject(r)}):e.invalid(fc(i))}static fromMillis(t,r={}){if(ao(t))return t<-C0||t>C0?e.invalid(\"Timestamp out of range\"):new e({ts:t,zone:ba(r.zone,hn.defaultZone),loc:mn.fromObject(r)});throw new vr(`fromMillis requires a numerical input, but received a ${typeof t} with value ${t}`)}static fromSeconds(t,r={}){if(ao(t))return new e({ts:t*1e3,zone:ba(r.zone,hn.defaultZone),loc:mn.fromObject(r)});throw new vr(\"fromSeconds requires a numerical input\")}static fromObject(t,r={}){t=t||{};let n=ba(r.zone,hn.defaultZone);if(!n.isValid)return e.invalid(fc(n));let i=hn.now(),a=kt(r.specificOffset)?n.offset(i):r.specificOffset,o=Sc(t,T0),s=!kt(o.ordinal),u=!kt(o.year),l=!kt(o.month)||!kt(o.day),c=u||l,d=o.weekYear||o.weekNumber,m=mn.fromObject(r);if((c||s)&&d)throw new ro(\"Can't mix weekYear/weekNumber units with year/month/day or ordinals\");if(l&&s)throw new ro(\"Can't mix ordinal dates with month/day\");let h=d||o.weekday&&!c,g,y,v=hc(i,a);h?(g=NP,y=LP,v=km(v)):s?(g=RP,y=PP,v=sm(v)):(g=PS,y=LS);let D=!1;for(let B of g){let G=o[B];kt(G)?D?o[B]=y[B]:o[B]=v[B]:D=!0}let I=h?AP(o):s?OP(o):IS(o),C=I||AS(o);if(C)return e.invalid(C);let x=h?k0(o):s?x0(o):o,[O,A]=gc(x,a,n),P=new e({ts:O,zone:n,o:A,loc:m});return o.weekday&&c&&t.weekday!==P.weekday?e.invalid(\"mismatched weekday\",`you can't specify both a weekday of ${o.weekday} and a date of ${P.toISO()}`):P}static fromISO(t,r={}){let[n,i]=eP(t);return Sl(n,i,r,\"ISO 8601\",t)}static fromRFC2822(t,r={}){let[n,i]=tP(t);return Sl(n,i,r,\"RFC 2822\",t)}static fromHTTP(t,r={}){let[n,i]=nP(t);return Sl(n,i,r,\"HTTP\",r)}static fromFormat(t,r,n={}){if(kt(t)||kt(r))throw new vr(\"fromFormat requires an input string and a format\");let{locale:i=null,numberingSystem:a=null}=n,o=mn.fromOpts({locale:i,numberingSystem:a,defaultToEN:!0}),[s,u,l,c]=IP(o,t,r);return c?e.invalid(c):Sl(s,u,n,`format ${r}`,t,l)}static fromString(t,r,n={}){return e.fromFormat(t,r,n)}static fromSQL(t,r={}){let[n,i]=uP(t);return Sl(n,i,r,\"SQL\",t)}static invalid(t,r=null){if(!t)throw new vr(\"need to specify a reason the DateTime is invalid\");let n=t instanceof Ar?t:new Ar(t,r);if(hn.throwOnInvalid)throw new fm(n);return new e({invalid:n})}static isDateTime(t){return t&&t.isLuxonDateTime||!1}static parseFormatForOpts(t,r={}){let n=xS(t,mn.fromObject(r));return n?n.map(i=>i?i.val:null).join(\"\"):null}static expandFormat(t,r={}){return ES(Ir.parseFormat(t),mn.fromObject(r)).map(i=>i.val).join(\"\")}get(t){return this[t]}get isValid(){return this.invalid===null}get invalidReason(){return this.invalid?this.invalid.reason:null}get invalidExplanation(){return this.invalid?this.invalid.explanation:null}get locale(){return this.isValid?this.loc.locale:null}get numberingSystem(){return this.isValid?this.loc.numberingSystem:null}get outputCalendar(){return this.isValid?this.loc.outputCalendar:null}get zone(){return this._zone}get zoneName(){return this.isValid?this.zone.name:null}get year(){return this.isValid?this.c.year:NaN}get quarter(){return this.isValid?Math.ceil(this.c.month/3):NaN}get month(){return this.isValid?this.c.month:NaN}get day(){return this.isValid?this.c.day:NaN}get hour(){return this.isValid?this.c.hour:NaN}get minute(){return this.isValid?this.c.minute:NaN}get second(){return this.isValid?this.c.second:NaN}get millisecond(){return this.isValid?this.c.millisecond:NaN}get weekYear(){return this.isValid?um(this).weekYear:NaN}get weekNumber(){return this.isValid?um(this).weekNumber:NaN}get weekday(){return this.isValid?um(this).weekday:NaN}get ordinal(){return this.isValid?sm(this.c).ordinal:NaN}get monthShort(){return this.isValid?es.months(\"short\",{locObj:this.loc})[this.month-1]:null}get monthLong(){return this.isValid?es.months(\"long\",{locObj:this.loc})[this.month-1]:null}get weekdayShort(){return this.isValid?es.weekdays(\"short\",{locObj:this.loc})[this.weekday-1]:null}get weekdayLong(){return this.isValid?es.weekdays(\"long\",{locObj:this.loc})[this.weekday-1]:null}get offset(){return this.isValid?+this.o:NaN}get offsetNameShort(){return this.isValid?this.zone.offsetName(this.ts,{format:\"short\",locale:this.locale}):null}get offsetNameLong(){return this.isValid?this.zone.offsetName(this.ts,{format:\"long\",locale:this.locale}):null}get isOffsetFixed(){return this.isValid?this.zone.isUniversal:null}get isInDST(){return this.isOffsetFixed?!1:this.offset>this.set({month:1,day:1}).offset||this.offset>this.set({month:5}).offset}getPossibleOffsets(){if(!this.isValid||this.isOffsetFixed)return[this];let t=864e5,r=6e4,n=Mc(this.c),i=this.zone.offset(n-t),a=this.zone.offset(n+t),o=this.zone.offset(n-i*r),s=this.zone.offset(n-a*r);if(o===s)return[this];let u=n-o*r,l=n-s*r,c=hc(u,o),d=hc(l,s);return c.hour===d.hour&&c.minute===d.minute&&c.second===d.second&&c.millisecond===d.millisecond?[no(this,{ts:u}),no(this,{ts:l})]:[this]}get isInLeapYear(){return Fl(this.year)}get daysInMonth(){return bc(this.year,this.month)}get daysInYear(){return this.isValid?Cl(this.year):NaN}get weeksInWeekYear(){return this.isValid?Dc(this.weekYear):NaN}resolvedLocaleOptions(t={}){let{locale:r,numberingSystem:n,calendar:i}=Ir.create(this.loc.clone(t),t).resolvedOptions(this);return{locale:r,numberingSystem:n,outputCalendar:i}}toUTC(t=0,r={}){return this.setZone(Kr.instance(t),r)}toLocal(){return this.setZone(hn.defaultZone)}setZone(t,{keepLocalTime:r=!1,keepCalendarTime:n=!1}={}){if(t=ba(t,hn.defaultZone),t.equals(this.zone))return this;if(t.isValid){let i=this.ts;if(r||n){let a=t.offset(this.ts),o=this.toObject();[i]=gc(o,a,t)}return no(this,{ts:i,zone:t})}else return e.invalid(fc(t))}reconfigure({locale:t,numberingSystem:r,outputCalendar:n}={}){let i=this.loc.clone({locale:t,numberingSystem:r,outputCalendar:n});return no(this,{loc:i})}setLocale(t){return this.reconfigure({locale:t})}set(t){if(!this.isValid)return this;let r=Sc(t,T0),n=!kt(r.weekYear)||!kt(r.weekNumber)||!kt(r.weekday),i=!kt(r.ordinal),a=!kt(r.year),o=!kt(r.month)||!kt(r.day),s=a||o,u=r.weekYear||r.weekNumber;if((s||i)&&u)throw new ro(\"Can't mix weekYear/weekNumber units with year/month/day or ordinals\");if(o&&i)throw new ro(\"Can't mix ordinal dates with month/day\");let l;n?l=k0({...km(this.c),...r}):kt(r.ordinal)?(l={...this.toObject(),...r},kt(r.day)&&(l.day=Math.min(bc(l.year,l.month),l.day))):l=x0({...sm(this.c),...r});let[c,d]=gc(l,this.o,this.zone);return no(this,{ts:c,o:d})}plus(t){if(!this.isValid)return this;let r=He.fromDurationLike(t);return no(this,_0(this,r))}minus(t){if(!this.isValid)return this;let r=He.fromDurationLike(t).negate();return no(this,_0(this,r))}startOf(t){if(!this.isValid)return this;let r={},n=He.normalizeUnit(t);switch(n){case\"years\":r.month=1;case\"quarters\":case\"months\":r.day=1;case\"weeks\":case\"days\":r.hour=0;case\"hours\":r.minute=0;case\"minutes\":r.second=0;case\"seconds\":r.millisecond=0;break}if(n===\"weeks\"&&(r.weekday=1),n===\"quarters\"){let i=Math.ceil(this.month/3);r.month=(i-1)*3+1}return this.set(r)}endOf(t){return this.isValid?this.plus({[t]:1}).startOf(t).minus(1):this}toFormat(t,r={}){return this.isValid?Ir.create(this.loc.redefaultToEN(r)).formatDateTimeFromString(this,t):lm}toLocaleString(t=vc,r={}){return this.isValid?Ir.create(this.loc.clone(r),t).formatDateTime(this):lm}toLocaleParts(t={}){return this.isValid?Ir.create(this.loc.clone(t),t).formatDateTimeParts(this):[]}toISO({format:t=\"extended\",suppressSeconds:r=!1,suppressMilliseconds:n=!1,includeOffset:i=!0,extendedZone:a=!1}={}){if(!this.isValid)return null;let o=t===\"extended\",s=cm(this,o);return s+=\"T\",s+=M0(this,o,r,n,i,a),s}toISODate({format:t=\"extended\"}={}){return this.isValid?cm(this,t===\"extended\"):null}toISOWeekDate(){return mc(this,\"kkkk-'W'WW-c\")}toISOTime({suppressMilliseconds:t=!1,suppressSeconds:r=!1,includeOffset:n=!0,includePrefix:i=!1,extendedZone:a=!1,format:o=\"extended\"}={}){return this.isValid?(i?\"T\":\"\")+M0(this,o===\"extended\",r,t,n,a):null}toRFC2822(){return mc(this,\"EEE, dd LLL yyyy HH:mm:ss ZZZ\",!1)}toHTTP(){return mc(this.toUTC(),\"EEE, dd LLL yyyy HH:mm:ss 'GMT'\")}toSQLDate(){return this.isValid?cm(this,!0):null}toSQLTime({includeOffset:t=!0,includeZone:r=!1,includeOffsetSpace:n=!0}={}){let i=\"HH:mm:ss.SSS\";return(r||t)&&(n&&(i+=\" \"),r?i+=\"z\":t&&(i+=\"ZZ\")),mc(this,i,!0)}toSQL(t={}){return this.isValid?`${this.toSQLDate()} ${this.toSQLTime(t)}`:null}toString(){return this.isValid?this.toISO():lm}valueOf(){return this.toMillis()}toMillis(){return this.isValid?this.ts:NaN}toSeconds(){return this.isValid?this.ts/1e3:NaN}toUnixInteger(){return this.isValid?Math.floor(this.ts/1e3):NaN}toJSON(){return this.toISO()}toBSON(){return this.toJSDate()}toObject(t={}){if(!this.isValid)return{};let r={...this.c};return t.includeConfig&&(r.outputCalendar=this.outputCalendar,r.numberingSystem=this.loc.numberingSystem,r.locale=this.loc.locale),r}toJSDate(){return new Date(this.isValid?this.ts:NaN)}diff(t,r=\"milliseconds\",n={}){if(!this.isValid||!t.isValid)return He.invalid(\"created by diffing an invalid DateTime\");let i={locale:this.locale,numberingSystem:this.numberingSystem,...n},a=f2(r).map(He.normalizeUnit),o=t.valueOf()>this.valueOf(),s=o?this:t,u=o?t:this,l=gP(s,u,a,i);return o?l.negate():l}diffNow(t=\"milliseconds\",r={}){return this.diff(e.now(),t,r)}until(t){return this.isValid?ns.fromDateTimes(this,t):this}hasSame(t,r){if(!this.isValid)return!1;let n=t.valueOf(),i=this.setZone(t.zone,{keepLocalTime:!0});return i.startOf(r)<=n&&n<=i.endOf(r)}equals(t){return this.isValid&&t.isValid&&this.valueOf()===t.valueOf()&&this.zone.equals(t.zone)&&this.loc.equals(t.loc)}toRelative(t={}){if(!this.isValid)return null;let r=t.base||e.fromObject({},{zone:this.zone}),n=t.padding?thisr.valueOf(),Math.min)}static max(...t){if(!t.every(e.isDateTime))throw new vr(\"max requires all arguments be DateTimes\");return p0(t,r=>r.valueOf(),Math.max)}static fromFormatExplain(t,r,n={}){let{locale:i=null,numberingSystem:a=null}=n,o=mn.fromOpts({locale:i,numberingSystem:a,defaultToEN:!0});return kS(o,t,r)}static fromStringExplain(t,r,n={}){return e.fromFormatExplain(t,r,n)}static get DATE_SHORT(){return vc}static get DATE_MED(){return H0}static get DATE_MED_WITH_WEEKDAY(){return GL}static get DATE_FULL(){return B0}static get DATE_HUGE(){return V0}static get TIME_SIMPLE(){return $0}static get TIME_WITH_SECONDS(){return U0}static get TIME_WITH_SHORT_OFFSET(){return W0}static get TIME_WITH_LONG_OFFSET(){return Y0}static get TIME_24_SIMPLE(){return z0}static get TIME_24_WITH_SECONDS(){return K0}static get TIME_24_WITH_SHORT_OFFSET(){return j0}static get TIME_24_WITH_LONG_OFFSET(){return q0}static get DATETIME_SHORT(){return G0}static get DATETIME_SHORT_WITH_SECONDS(){return J0}static get DATETIME_MED(){return Z0}static get DATETIME_MED_WITH_SECONDS(){return Q0}static get DATETIME_MED_WITH_WEEKDAY(){return JL}static get DATETIME_FULL(){return X0}static get DATETIME_FULL_WITH_SECONDS(){return eS}static get DATETIME_HUGE(){return tS}static get DATETIME_HUGE_WITH_SECONDS(){return nS}};function El(e){if(mt.isDateTime(e))return e;if(e&&e.valueOf&&ao(e.valueOf()))return mt.fromJSDate(e);if(e&&typeof e==\"object\")return mt.fromObject(e);throw new vr(`Unknown datetime argument: ${e}, of type ${typeof e}`)}var Hm={renderNullAs:\"\\\\-\",taskCompletionTracking:!1,taskCompletionUseEmojiShorthand:!1,taskCompletionText:\"completion\",taskCompletionDateFormat:\"yyyy-MM-dd\",recursiveSubTaskCompletion:!1,warnOnEmptyResult:!0,refreshEnabled:!0,refreshInterval:2500,defaultDateFormat:\"MMMM dd, yyyy\",defaultDateTimeFormat:\"h:mm a - MMMM dd, yyyy\",maxRecursiveRenderDepth:4,tableIdColumnName:\"File\",tableGroupColumnName:\"Group\",showResultCount:!0},HP={allowHtml:!0};({...Hm,...HP});var xm=class e{constructor(t){ur(this,\"value\");ur(this,\"successful\");this.value=t,this.successful=!0}map(t){return new e(t(this.value))}flatMap(t){return t(this.value)}mapErr(t){return this}bimap(t,r){return this.map(t)}orElse(t){return this.value}cast(){return this}orElseThrow(t){return this.value}},Cm=class e{constructor(t){ur(this,\"error\");ur(this,\"successful\");this.error=t,this.successful=!1}map(t){return this}flatMap(t){return this}mapErr(t){return new e(t(this.error))}bimap(t,r){return this.mapErr(r)}orElse(t){return t}cast(){return this}orElseThrow(t){throw t?new Error(t(this.error)):new Error(\"\"+this.error)}},Ec;(function(e){function t(a){return new xm(a)}e.success=t;function r(a){return new Cm(a)}e.failure=r;function n(a,o,s){return a.successful?o.successful?s(a.value,o.value):r(o.error):r(a.error)}e.flatMap2=n;function i(a,o,s){return n(a,o,(u,l)=>t(s(u,l)))}e.map2=i})(Ec||(Ec={}));var BP=typeof globalThis!=\"undefined\"?globalThis:typeof window!=\"undefined\"||typeof window!=\"undefined\"?window:typeof self!=\"undefined\"?self:{},kc={exports:{}};kc.exports;(function(e,t){(function(r,n){e.exports=n()})(typeof self!=\"undefined\"?self:BP,function(){return function(r){var n={};function i(a){if(n[a])return n[a].exports;var o=n[a]={i:a,l:!1,exports:{}};return r[a].call(o.exports,o,o.exports,i),o.l=!0,o.exports}return i.m=r,i.c=n,i.d=function(a,o,s){i.o(a,o)||Object.defineProperty(a,o,{configurable:!1,enumerable:!0,get:s})},i.r=function(a){Object.defineProperty(a,\"__esModule\",{value:!0})},i.n=function(a){var o=a&&a.__esModule?function(){return a.default}:function(){return a};return i.d(o,\"a\",o),o},i.o=function(a,o){return Object.prototype.hasOwnProperty.call(a,o)},i.p=\"\",i(i.s=0)}([function(r,n,i){function a(L){if(!(this instanceof a))return new a(L);this._=L}var o=a.prototype;function s(L,K){for(var ee=0;ee>7),buf:function(de){var ve=u(function(ge,M,H,q){return ge.concat(H===q.length-1?Buffer.from([M,0]).readUInt16BE(0):q.readUInt16BE(H))},[],de);return Buffer.from(l(function(ge){return(ge<<1&65535)>>8},ve))}(ee.buf)}}),ee}function d(){return typeof Buffer!=\"undefined\"}function m(){if(!d())throw new Error(\"Buffer global does not exist; please use webpack if you need to parse Buffers in the browser.\")}function h(L){m();var K=u(function(ve,ge){return ve+ge},0,L);if(K%8!=0)throw new Error(\"The bits [\"+L.join(\", \")+\"] add up to \"+K+\" which is not an even number of bytes; the total should be divisible by 8\");var ee,ue=K/8,de=(ee=function(ve){return ve>48},u(function(ve,ge){return ve||(ee(ge)?ge:ve)},null,L));if(de)throw new Error(de+\" bit range requested exceeds 48 bit (6 byte) Number max.\");return new a(function(ve,ge){var M=ue+ge;return M>ve.length?B(ge,ue.toString()+\" bytes\"):P(M,u(function(H,q){var ie=c(q,H.buf);return{coll:H.coll.concat(ie.v),buf:ie.buf}},{coll:[],buf:ve.slice(ge,M)},L).coll)})}function g(L,K){return new a(function(ee,ue){return m(),ue+K>ee.length?B(ue,K+\" bytes for \"+L):P(ue+K,ee.slice(ue,ue+K))})}function y(L,K){if(typeof(ee=K)!=\"number\"||Math.floor(ee)!==ee||K<0||K>6)throw new Error(L+\" requires integer length in range [0, 6].\");var ee}function v(L){return y(\"uintBE\",L),g(\"uintBE(\"+L+\")\",L).map(function(K){return K.readUIntBE(0,L)})}function D(L){return y(\"uintLE\",L),g(\"uintLE(\"+L+\")\",L).map(function(K){return K.readUIntLE(0,L)})}function I(L){return y(\"intBE\",L),g(\"intBE(\"+L+\")\",L).map(function(K){return K.readIntBE(0,L)})}function C(L){return y(\"intLE\",L),g(\"intLE(\"+L+\")\",L).map(function(K){return K.readIntLE(0,L)})}function x(L){return L instanceof a}function O(L){return{}.toString.call(L)===\"[object Array]\"}function A(L){return d()&&Buffer.isBuffer(L)}function P(L,K){return{status:!0,index:L,value:K,furthest:-1,expected:[]}}function B(L,K){return O(K)||(K=[K]),{status:!1,index:-1,value:null,furthest:L,expected:K}}function G(L,K){if(!K||L.furthest>K.furthest)return L;var ee=L.furthest===K.furthest?function(ue,de){if(function(){if(a._supportsSet!==void 0)return a._supportsSet;var ae=typeof Set!=\"undefined\";return a._supportsSet=ae,ae}()&&Array.from){for(var ve=new Set(ue),ge=0;ge=0;){if(ge in ee){ue=ee[ge].line,ve===0&&(ve=ee[ge].lineStart);break}(L.charAt(ge)===`\n`||L.charAt(ge)===\"\\r\"&&L.charAt(ge+1)!==`\n`)&&(de++,ve===0&&(ve=ge+1)),ge--}var M=ue+de,H=K-ve;return ee[K]={line:M,lineStart:ve},{offset:K,line:M+1,column:H+1}}function oe(L){if(!x(L))throw new Error(\"not a parser: \"+L)}function te(L,K){return typeof L==\"string\"?L.charAt(K):L[K]}function re(L){if(typeof L!=\"number\")throw new Error(\"not a number: \"+L)}function ne(L){if(typeof L!=\"function\")throw new Error(\"not a function: \"+L)}function be(L){if(typeof L!=\"string\")throw new Error(\"not a string: \"+L)}var pe=2,De=3,Ce=8,U=5*Ce,Je=4*Ce,it=\" \";function N(L,K){return new Array(K+1).join(L)}function Ze(L,K,ee){var ue=K-L.length;return ue<=0?L:N(ee,ue)+L}function It(L,K,ee,ue){return{from:L-K>0?L-K:0,to:L+ee>ue?ue:L+ee}}function Mt(L,K){var ee,ue,de,ve,ge,M=K.index,H=M.offset,q=1;if(H===L.length)return\"Got the end of the input\";if(A(L)){var ie=H-H%Ce,ye=H-ie,ce=It(ie,U,Je+Ce,L.length),ae=l(function(Te){return l(function(Ue){return Ze(Ue.toString(16),2,\"0\")},Te)},function(Te,Ue){var We=Te.length,Ft=[],Hn=0;if(We<=Ue)return[Te.slice()];for(var Ot=0;Ot=4&&(ee+=1),q=2,de=l(function(Te){return Te.length<=4?Te.join(\" \"):Te.slice(0,4).join(\" \")+\" \"+Te.slice(4).join(\" \")},ae),(ge=(8*(ve.to>0?ve.to-1:ve.to)).toString(16).length)<2&&(ge=2)}else{var Se=L.split(/\\r\\n|[\\n\\r\\u2028\\u2029]/);ee=M.column-1,ue=M.line-1,ve=It(ue,pe,De,Se.length),de=Se.slice(ve.from,ve.to),ge=ve.to.toString().length}var nt=ue-ve.from;return A(L)&&(ge=(8*(ve.to>0?ve.to-1:ve.to)).toString(16).length)<2&&(ge=2),u(function(Te,Ue,We){var Ft,Hn=We===nt,Ot=Hn?\"> \":it;return Ft=A(L)?Ze((8*(ve.from+We)).toString(16),ge,\"0\"):Ze((ve.from+We+1).toString(),ge,\" \"),[].concat(Te,[Ot+Ft+\" | \"+Ue],Hn?[it+N(\" \",ge)+\" | \"+Ze(\"\",ee,\" \")+N(\"^\",q)]:[])},[],de).join(`\n`)}function jt(L,K){return[`\n`,\"-- PARSING FAILED \"+N(\"-\",50),`\n\n`,Mt(L,K),`\n\n`,(ee=K.expected,ee.length===1?`Expected:\n\n`+ee[0]:`Expected one of the following: \n\n`+ee.join(\", \")),`\n`].join(\"\");var ee}function vt(L){return L.flags!==void 0?L.flags:[L.global?\"g\":\"\",L.ignoreCase?\"i\":\"\",L.multiline?\"m\":\"\",L.unicode?\"u\":\"\",L.sticky?\"y\":\"\"].join(\"\")}function Wt(){for(var L=[].slice.call(arguments),K=L.length,ee=0;ee=2?re(K):K=0;var ee=function(de){return RegExp(\"^(?:\"+de.source+\")\",vt(de))}(L),ue=\"\"+L;return a(function(de,ve){var ge=ee.exec(de.slice(ve));if(ge){if(0<=K&&K<=ge.length){var M=ge[0],H=ge[K];return P(ve+M.length,H)}return B(ve,\"valid match group (0 to \"+ge.length+\") in \"+ue)}return B(ve,ue)})}function xn(L){return a(function(K,ee){return P(ee,L)})}function jn(L){return a(function(K,ee){return B(ee,L)})}function sn(L){if(x(L))return a(function(K,ee){var ue=L._(K,ee);return ue.index=ee,ue.value=\"\",ue});if(typeof L==\"string\")return sn(yn(L));if(L instanceof RegExp)return sn(vn(L));throw new Error(\"not a string, regexp, or parser: \"+L)}function Rt(L){return oe(L),a(function(K,ee){var ue=L._(K,ee),de=K.slice(ee,ue.index);return ue.status?B(ee,'not \"'+de+'\"'):P(ee,null)})}function Vt(L){return ne(L),a(function(K,ee){var ue=te(K,ee);return ee=L.length?B(K,\"any character/byte\"):P(K+1,te(L,K))}),rn=a(function(L,K){return P(L.length,L.slice(K))}),At=a(function(L,K){return K=0}).desc(K)},a.optWhitespace=Dt,a.Parser=a,a.range=function(L,K){return Vt(function(ee){return L<=ee&&ee<=K}).desc(L+\"-\"+K)},a.regex=vn,a.regexp=vn,a.sepBy=en,a.sepBy1=gn,a.seq=Wt,a.seqMap=Tt,a.seqObj=function(){for(var L,K={},ee=0,ue=(L=arguments,Array.prototype.slice.call(L)),de=ue.length,ve=0;ve255)throw new Error(\"Value specified to byte constructor (\"+L+\"=0x\"+L.toString(16)+\") is larger in value than a single byte.\");var K=(L>15?\"0x\":\"0x0\")+L.toString(16);return a(function(ee,ue){var de=te(ee,ue);return de===L?P(ue+1,de):B(ue,K)})},buffer:function(L){return g(\"buffer\",L).map(function(K){return Buffer.from(K)})},encodedString:function(L,K){return g(\"string\",K).map(function(ee){return ee.toString(L)})},uintBE:v,uint8BE:v(1),uint16BE:v(2),uint32BE:v(4),uintLE:D,uint8LE:D(1),uint16LE:D(2),uint32LE:D(4),intBE:I,int8BE:I(1),int16BE:I(2),int32BE:I(4),intLE:C,int8LE:C(1),int16LE:C(2),int32LE:C(4),floatBE:g(\"floatBE\",4).map(function(L){return L.readFloatBE(0)}),floatLE:g(\"floatLE\",4).map(function(L){return L.readFloatLE(0)}),doubleBE:g(\"doubleBE\",8).map(function(L){return L.readDoubleBE(0)}),doubleLE:g(\"doubleLE\",8).map(function(L){return L.readDoubleLE(0)})},r.exports=a}])})})(kc,kc.exports);var W=kc.exports,Bm=()=>/[#*0-9]\\uFE0F?\\u20E3|[\\xA9\\xAE\\u203C\\u2049\\u2122\\u2139\\u2194-\\u2199\\u21A9\\u21AA\\u231A\\u231B\\u2328\\u23CF\\u23ED-\\u23EF\\u23F1\\u23F2\\u23F8-\\u23FA\\u24C2\\u25AA\\u25AB\\u25B6\\u25C0\\u25FB\\u25FC\\u25FE\\u2600-\\u2604\\u260E\\u2611\\u2614\\u2615\\u2618\\u2620\\u2622\\u2623\\u2626\\u262A\\u262E\\u262F\\u2638-\\u263A\\u2640\\u2642\\u2648-\\u2653\\u265F\\u2660\\u2663\\u2665\\u2666\\u2668\\u267B\\u267E\\u267F\\u2692\\u2694-\\u2697\\u2699\\u269B\\u269C\\u26A0\\u26A7\\u26AA\\u26B0\\u26B1\\u26BD\\u26BE\\u26C4\\u26C8\\u26CF\\u26D1\\u26D3\\u26E9\\u26F0-\\u26F5\\u26F7\\u26F8\\u26FA\\u2702\\u2708\\u2709\\u270F\\u2712\\u2714\\u2716\\u271D\\u2721\\u2733\\u2734\\u2744\\u2747\\u2757\\u2763\\u27A1\\u2934\\u2935\\u2B05-\\u2B07\\u2B1B\\u2B1C\\u2B55\\u3030\\u303D\\u3297\\u3299]\\uFE0F?|[\\u261D\\u270C\\u270D](?:\\uFE0F|\\uD83C[\\uDFFB-\\uDFFF])?|[\\u270A\\u270B](?:\\uD83C[\\uDFFB-\\uDFFF])?|[\\u23E9-\\u23EC\\u23F0\\u23F3\\u25FD\\u2693\\u26A1\\u26AB\\u26C5\\u26CE\\u26D4\\u26EA\\u26FD\\u2705\\u2728\\u274C\\u274E\\u2753-\\u2755\\u2795-\\u2797\\u27B0\\u27BF\\u2B50]|\\u26F9(?:\\uFE0F|\\uD83C[\\uDFFB-\\uDFFF])?(?:\\u200D[\\u2640\\u2642]\\uFE0F?)?|\\u2764\\uFE0F?(?:\\u200D(?:\\uD83D\\uDD25|\\uD83E\\uDE79))?|\\uD83C(?:[\\uDC04\\uDD70\\uDD71\\uDD7E\\uDD7F\\uDE02\\uDE37\\uDF21\\uDF24-\\uDF2C\\uDF36\\uDF7D\\uDF96\\uDF97\\uDF99-\\uDF9B\\uDF9E\\uDF9F\\uDFCD\\uDFCE\\uDFD4-\\uDFDF\\uDFF5\\uDFF7]\\uFE0F?|[\\uDF85\\uDFC2\\uDFC7](?:\\uD83C[\\uDFFB-\\uDFFF])?|[\\uDFC3\\uDFC4\\uDFCA](?:\\uD83C[\\uDFFB-\\uDFFF])?(?:\\u200D[\\u2640\\u2642]\\uFE0F?)?|[\\uDFCB\\uDFCC](?:\\uFE0F|\\uD83C[\\uDFFB-\\uDFFF])?(?:\\u200D[\\u2640\\u2642]\\uFE0F?)?|[\\uDCCF\\uDD8E\\uDD91-\\uDD9A\\uDE01\\uDE1A\\uDE2F\\uDE32-\\uDE36\\uDE38-\\uDE3A\\uDE50\\uDE51\\uDF00-\\uDF20\\uDF2D-\\uDF35\\uDF37-\\uDF7C\\uDF7E-\\uDF84\\uDF86-\\uDF93\\uDFA0-\\uDFC1\\uDFC5\\uDFC6\\uDFC8\\uDFC9\\uDFCF-\\uDFD3\\uDFE0-\\uDFF0\\uDFF8-\\uDFFF]|\\uDDE6\\uD83C[\\uDDE8-\\uDDEC\\uDDEE\\uDDF1\\uDDF2\\uDDF4\\uDDF6-\\uDDFA\\uDDFC\\uDDFD\\uDDFF]|\\uDDE7\\uD83C[\\uDDE6\\uDDE7\\uDDE9-\\uDDEF\\uDDF1-\\uDDF4\\uDDF6-\\uDDF9\\uDDFB\\uDDFC\\uDDFE\\uDDFF]|\\uDDE8\\uD83C[\\uDDE6\\uDDE8\\uDDE9\\uDDEB-\\uDDEE\\uDDF0-\\uDDF5\\uDDF7\\uDDFA-\\uDDFF]|\\uDDE9\\uD83C[\\uDDEA\\uDDEC\\uDDEF\\uDDF0\\uDDF2\\uDDF4\\uDDFF]|\\uDDEA\\uD83C[\\uDDE6\\uDDE8\\uDDEA\\uDDEC\\uDDED\\uDDF7-\\uDDFA]|\\uDDEB\\uD83C[\\uDDEE-\\uDDF0\\uDDF2\\uDDF4\\uDDF7]|\\uDDEC\\uD83C[\\uDDE6\\uDDE7\\uDDE9-\\uDDEE\\uDDF1-\\uDDF3\\uDDF5-\\uDDFA\\uDDFC\\uDDFE]|\\uDDED\\uD83C[\\uDDF0\\uDDF2\\uDDF3\\uDDF7\\uDDF9\\uDDFA]|\\uDDEE\\uD83C[\\uDDE8-\\uDDEA\\uDDF1-\\uDDF4\\uDDF6-\\uDDF9]|\\uDDEF\\uD83C[\\uDDEA\\uDDF2\\uDDF4\\uDDF5]|\\uDDF0\\uD83C[\\uDDEA\\uDDEC-\\uDDEE\\uDDF2\\uDDF3\\uDDF5\\uDDF7\\uDDFC\\uDDFE\\uDDFF]|\\uDDF1\\uD83C[\\uDDE6-\\uDDE8\\uDDEE\\uDDF0\\uDDF7-\\uDDFB\\uDDFE]|\\uDDF2\\uD83C[\\uDDE6\\uDDE8-\\uDDED\\uDDF0-\\uDDFF]|\\uDDF3\\uD83C[\\uDDE6\\uDDE8\\uDDEA-\\uDDEC\\uDDEE\\uDDF1\\uDDF4\\uDDF5\\uDDF7\\uDDFA\\uDDFF]|\\uDDF4\\uD83C\\uDDF2|\\uDDF5\\uD83C[\\uDDE6\\uDDEA-\\uDDED\\uDDF0-\\uDDF3\\uDDF7-\\uDDF9\\uDDFC\\uDDFE]|\\uDDF6\\uD83C\\uDDE6|\\uDDF7\\uD83C[\\uDDEA\\uDDF4\\uDDF8\\uDDFA\\uDDFC]|\\uDDF8\\uD83C[\\uDDE6-\\uDDEA\\uDDEC-\\uDDF4\\uDDF7-\\uDDF9\\uDDFB\\uDDFD-\\uDDFF]|\\uDDF9\\uD83C[\\uDDE6\\uDDE8\\uDDE9\\uDDEB-\\uDDED\\uDDEF-\\uDDF4\\uDDF7\\uDDF9\\uDDFB\\uDDFC\\uDDFF]|\\uDDFA\\uD83C[\\uDDE6\\uDDEC\\uDDF2\\uDDF3\\uDDF8\\uDDFE\\uDDFF]|\\uDDFB\\uD83C[\\uDDE6\\uDDE8\\uDDEA\\uDDEC\\uDDEE\\uDDF3\\uDDFA]|\\uDDFC\\uD83C[\\uDDEB\\uDDF8]|\\uDDFD\\uD83C\\uDDF0|\\uDDFE\\uD83C[\\uDDEA\\uDDF9]|\\uDDFF\\uD83C[\\uDDE6\\uDDF2\\uDDFC]|\\uDFF3\\uFE0F?(?:\\u200D(?:\\u26A7\\uFE0F?|\\uD83C\\uDF08))?|\\uDFF4(?:\\u200D\\u2620\\uFE0F?|\\uDB40\\uDC67\\uDB40\\uDC62\\uDB40(?:\\uDC65\\uDB40\\uDC6E\\uDB40\\uDC67|\\uDC73\\uDB40\\uDC63\\uDB40\\uDC74|\\uDC77\\uDB40\\uDC6C\\uDB40\\uDC73)\\uDB40\\uDC7F)?)|\\uD83D(?:[\\uDC08\\uDC26](?:\\u200D\\u2B1B)?|[\\uDC3F\\uDCFD\\uDD49\\uDD4A\\uDD6F\\uDD70\\uDD73\\uDD76-\\uDD79\\uDD87\\uDD8A-\\uDD8D\\uDDA5\\uDDA8\\uDDB1\\uDDB2\\uDDBC\\uDDC2-\\uDDC4\\uDDD1-\\uDDD3\\uDDDC-\\uDDDE\\uDDE1\\uDDE3\\uDDE8\\uDDEF\\uDDF3\\uDDFA\\uDECB\\uDECD-\\uDECF\\uDEE0-\\uDEE5\\uDEE9\\uDEF0\\uDEF3]\\uFE0F?|[\\uDC42\\uDC43\\uDC46-\\uDC50\\uDC66\\uDC67\\uDC6B-\\uDC6D\\uDC72\\uDC74-\\uDC76\\uDC78\\uDC7C\\uDC83\\uDC85\\uDC8F\\uDC91\\uDCAA\\uDD7A\\uDD95\\uDD96\\uDE4C\\uDE4F\\uDEC0\\uDECC](?:\\uD83C[\\uDFFB-\\uDFFF])?|[\\uDC6E\\uDC70\\uDC71\\uDC73\\uDC77\\uDC81\\uDC82\\uDC86\\uDC87\\uDE45-\\uDE47\\uDE4B\\uDE4D\\uDE4E\\uDEA3\\uDEB4-\\uDEB6](?:\\uD83C[\\uDFFB-\\uDFFF])?(?:\\u200D[\\u2640\\u2642]\\uFE0F?)?|[\\uDD74\\uDD90](?:\\uFE0F|\\uD83C[\\uDFFB-\\uDFFF])?|[\\uDC00-\\uDC07\\uDC09-\\uDC14\\uDC16-\\uDC25\\uDC27-\\uDC3A\\uDC3C-\\uDC3E\\uDC40\\uDC44\\uDC45\\uDC51-\\uDC65\\uDC6A\\uDC79-\\uDC7B\\uDC7D-\\uDC80\\uDC84\\uDC88-\\uDC8E\\uDC90\\uDC92-\\uDCA9\\uDCAB-\\uDCFC\\uDCFF-\\uDD3D\\uDD4B-\\uDD4E\\uDD50-\\uDD67\\uDDA4\\uDDFB-\\uDE2D\\uDE2F-\\uDE34\\uDE37-\\uDE44\\uDE48-\\uDE4A\\uDE80-\\uDEA2\\uDEA4-\\uDEB3\\uDEB7-\\uDEBF\\uDEC1-\\uDEC5\\uDED0-\\uDED2\\uDED5-\\uDED7\\uDEDC-\\uDEDF\\uDEEB\\uDEEC\\uDEF4-\\uDEFC\\uDFE0-\\uDFEB\\uDFF0]|\\uDC15(?:\\u200D\\uD83E\\uDDBA)?|\\uDC3B(?:\\u200D\\u2744\\uFE0F?)?|\\uDC41\\uFE0F?(?:\\u200D\\uD83D\\uDDE8\\uFE0F?)?|\\uDC68(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:\\uDC8B\\u200D\\uD83D)?\\uDC68|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D(?:[\\uDC68\\uDC69]\\u200D\\uD83D(?:\\uDC66(?:\\u200D\\uD83D\\uDC66)?|\\uDC67(?:\\u200D\\uD83D[\\uDC66\\uDC67])?)|[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uDC66(?:\\u200D\\uD83D\\uDC66)?|\\uDC67(?:\\u200D\\uD83D[\\uDC66\\uDC67])?)|\\uD83E[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD])|\\uD83C(?:\\uDFFB(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:\\uDC8B\\u200D\\uD83D)?\\uDC68\\uD83C[\\uDFFB-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83D\\uDC68\\uD83C[\\uDFFC-\\uDFFF])))?|\\uDFFC(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:\\uDC8B\\u200D\\uD83D)?\\uDC68\\uD83C[\\uDFFB-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83D\\uDC68\\uD83C[\\uDFFB\\uDFFD-\\uDFFF])))?|\\uDFFD(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:\\uDC8B\\u200D\\uD83D)?\\uDC68\\uD83C[\\uDFFB-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83D\\uDC68\\uD83C[\\uDFFB\\uDFFC\\uDFFE\\uDFFF])))?|\\uDFFE(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:\\uDC8B\\u200D\\uD83D)?\\uDC68\\uD83C[\\uDFFB-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83D\\uDC68\\uD83C[\\uDFFB-\\uDFFD\\uDFFF])))?|\\uDFFF(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:\\uDC8B\\u200D\\uD83D)?\\uDC68\\uD83C[\\uDFFB-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83D\\uDC68\\uD83C[\\uDFFB-\\uDFFE])))?))?|\\uDC69(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:\\uDC8B\\u200D\\uD83D)?[\\uDC68\\uDC69]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D(?:[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uDC66(?:\\u200D\\uD83D\\uDC66)?|\\uDC67(?:\\u200D\\uD83D[\\uDC66\\uDC67])?|\\uDC69\\u200D\\uD83D(?:\\uDC66(?:\\u200D\\uD83D\\uDC66)?|\\uDC67(?:\\u200D\\uD83D[\\uDC66\\uDC67])?))|\\uD83E[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD])|\\uD83C(?:\\uDFFB(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:[\\uDC68\\uDC69]|\\uDC8B\\u200D\\uD83D[\\uDC68\\uDC69])\\uD83C[\\uDFFB-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83D[\\uDC68\\uDC69]\\uD83C[\\uDFFC-\\uDFFF])))?|\\uDFFC(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:[\\uDC68\\uDC69]|\\uDC8B\\u200D\\uD83D[\\uDC68\\uDC69])\\uD83C[\\uDFFB-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83D[\\uDC68\\uDC69]\\uD83C[\\uDFFB\\uDFFD-\\uDFFF])))?|\\uDFFD(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:[\\uDC68\\uDC69]|\\uDC8B\\u200D\\uD83D[\\uDC68\\uDC69])\\uD83C[\\uDFFB-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83D[\\uDC68\\uDC69]\\uD83C[\\uDFFB\\uDFFC\\uDFFE\\uDFFF])))?|\\uDFFE(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:[\\uDC68\\uDC69]|\\uDC8B\\u200D\\uD83D[\\uDC68\\uDC69])\\uD83C[\\uDFFB-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83D[\\uDC68\\uDC69]\\uD83C[\\uDFFB-\\uDFFD\\uDFFF])))?|\\uDFFF(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D\\uD83D(?:[\\uDC68\\uDC69]|\\uDC8B\\u200D\\uD83D[\\uDC68\\uDC69])\\uD83C[\\uDFFB-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83D[\\uDC68\\uDC69]\\uD83C[\\uDFFB-\\uDFFE])))?))?|\\uDC6F(?:\\u200D[\\u2640\\u2642]\\uFE0F?)?|\\uDD75(?:\\uFE0F|\\uD83C[\\uDFFB-\\uDFFF])?(?:\\u200D[\\u2640\\u2642]\\uFE0F?)?|\\uDE2E(?:\\u200D\\uD83D\\uDCA8)?|\\uDE35(?:\\u200D\\uD83D\\uDCAB)?|\\uDE36(?:\\u200D\\uD83C\\uDF2B\\uFE0F?)?)|\\uD83E(?:[\\uDD0C\\uDD0F\\uDD18-\\uDD1F\\uDD30-\\uDD34\\uDD36\\uDD77\\uDDB5\\uDDB6\\uDDBB\\uDDD2\\uDDD3\\uDDD5\\uDEC3-\\uDEC5\\uDEF0\\uDEF2-\\uDEF8](?:\\uD83C[\\uDFFB-\\uDFFF])?|[\\uDD26\\uDD35\\uDD37-\\uDD39\\uDD3D\\uDD3E\\uDDB8\\uDDB9\\uDDCD-\\uDDCF\\uDDD4\\uDDD6-\\uDDDD](?:\\uD83C[\\uDFFB-\\uDFFF])?(?:\\u200D[\\u2640\\u2642]\\uFE0F?)?|[\\uDDDE\\uDDDF](?:\\u200D[\\u2640\\u2642]\\uFE0F?)?|[\\uDD0D\\uDD0E\\uDD10-\\uDD17\\uDD20-\\uDD25\\uDD27-\\uDD2F\\uDD3A\\uDD3F-\\uDD45\\uDD47-\\uDD76\\uDD78-\\uDDB4\\uDDB7\\uDDBA\\uDDBC-\\uDDCC\\uDDD0\\uDDE0-\\uDDFF\\uDE70-\\uDE7C\\uDE80-\\uDE88\\uDE90-\\uDEBD\\uDEBF-\\uDEC2\\uDECE-\\uDEDB\\uDEE0-\\uDEE8]|\\uDD3C(?:\\u200D[\\u2640\\u2642]\\uFE0F?|\\uD83C[\\uDFFB-\\uDFFF])?|\\uDDD1(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83E\\uDDD1))|\\uD83C(?:\\uDFFB(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D(?:\\uD83D\\uDC8B\\u200D)?\\uD83E\\uDDD1\\uD83C[\\uDFFC-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83E\\uDDD1\\uD83C[\\uDFFB-\\uDFFF])))?|\\uDFFC(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D(?:\\uD83D\\uDC8B\\u200D)?\\uD83E\\uDDD1\\uD83C[\\uDFFB\\uDFFD-\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83E\\uDDD1\\uD83C[\\uDFFB-\\uDFFF])))?|\\uDFFD(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D(?:\\uD83D\\uDC8B\\u200D)?\\uD83E\\uDDD1\\uD83C[\\uDFFB\\uDFFC\\uDFFE\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83E\\uDDD1\\uD83C[\\uDFFB-\\uDFFF])))?|\\uDFFE(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D(?:\\uD83D\\uDC8B\\u200D)?\\uD83E\\uDDD1\\uD83C[\\uDFFB-\\uDFFD\\uDFFF]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83E\\uDDD1\\uD83C[\\uDFFB-\\uDFFF])))?|\\uDFFF(?:\\u200D(?:[\\u2695\\u2696\\u2708]\\uFE0F?|\\u2764\\uFE0F?\\u200D(?:\\uD83D\\uDC8B\\u200D)?\\uD83E\\uDDD1\\uD83C[\\uDFFB-\\uDFFE]|\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED]|\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]|\\uD83E(?:[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]|\\uDD1D\\u200D\\uD83E\\uDDD1\\uD83C[\\uDFFB-\\uDFFF])))?))?|\\uDEF1(?:\\uD83C(?:\\uDFFB(?:\\u200D\\uD83E\\uDEF2\\uD83C[\\uDFFC-\\uDFFF])?|\\uDFFC(?:\\u200D\\uD83E\\uDEF2\\uD83C[\\uDFFB\\uDFFD-\\uDFFF])?|\\uDFFD(?:\\u200D\\uD83E\\uDEF2\\uD83C[\\uDFFB\\uDFFC\\uDFFE\\uDFFF])?|\\uDFFE(?:\\u200D\\uD83E\\uDEF2\\uD83C[\\uDFFB-\\uDFFD\\uDFFF])?|\\uDFFF(?:\\u200D\\uD83E\\uDEF2\\uD83C[\\uDFFB-\\uDFFE])?))?)/g;function NS(e){return e==null?e:e.shiftToAll().normalize()}function O0(e){return e.includes(\"/\")&&(e=e.substring(e.lastIndexOf(\"/\")+1)),e.endsWith(\".md\")&&(e=e.substring(0,e.length-3)),e}W.alt(W.regex(new RegExp(Bm(),\"\")),W.regex(/[0-9\\p{Letter}_-]+/u).map(e=>e.toLocaleLowerCase()),W.whitespace.map(e=>\"-\"),W.any.map(e=>\"\")).many().map(e=>e.join(\"\"));var VP=W.alt(W.regex(new RegExp(Bm(),\"\")),W.regex(/[0-9\\p{Letter}_-]+/u),W.whitespace.map(e=>\" \"),W.any.map(e=>\" \")).many().map(e=>e.join(\"\").split(/\\s+/).join(\" \").trim());function $P(e){return VP.tryParse(e)}function UP(e){return e=NS(e),e=He.fromObject(Object.fromEntries(Object.entries(e.toObject()).filter(([,t])=>t!=0))),e.toHuman()}var Ml;(function(e){function t(x,O=Hm,A=!1){let P=r(x);if(!P)return O.renderNullAs;switch(P.type){case\"null\":return O.renderNullAs;case\"string\":return P.value;case\"number\":case\"boolean\":return\"\"+P.value;case\"html\":return P.value.outerHTML;case\"widget\":return P.value.markdown();case\"link\":return P.value.markdown();case\"function\":return\"\";case\"array\":let B=\"\";return A&&(B+=\"[\"),B+=P.value.map(G=>t(G,O,!0)).join(\", \"),A&&(B+=\"]\"),B;case\"object\":return\"{ \"+Object.entries(P.value).map(G=>G[0]+\": \"+t(G[1],O,!0)).join(\", \")+\" }\";case\"date\":return P.value.second==0&&P.value.hour==0&&P.value.minute==0?P.value.toFormat(O.defaultDateFormat):P.value.toFormat(O.defaultDateTimeFormat);case\"duration\":return UP(P.value)}}e.toString=t;function r(x){return m(x)?{type:\"null\",value:x}:l(x)?{type:\"number\",value:x}:u(x)?{type:\"string\",value:x}:g(x)?{type:\"boolean\",value:x}:d(x)?{type:\"duration\",value:x}:c(x)?{type:\"date\",value:x}:v(x)?{type:\"widget\",value:x}:h(x)?{type:\"array\",value:x}:y(x)?{type:\"link\",value:x}:C(x)?{type:\"function\",value:x}:D(x)?{type:\"html\",value:x}:I(x)?{type:\"object\",value:x}:void 0}e.wrapValue=r;function n(x,O){if(I(x)){let A={};for(let[P,B]of Object.entries(x))A[P]=n(B,O);return A}else if(h(x)){let A=[];for(let P of x)A.push(n(P,O));return A}else return O(x)}e.mapLeaves=n;function i(x,O,A){var G,J;if(x===void 0&&(x=null),O===void 0&&(O=null),x===null&&O===null)return 0;if(x===null)return-1;if(O===null)return 1;let P=r(x),B=r(O);if(P===void 0&&B===void 0)return 0;if(P===void 0)return-1;if(B===void 0)return 1;if(P.type!=B.type)return P.type.localeCompare(B.type);if(P.value===B.value)return 0;switch(P.type){case\"string\":return P.value.localeCompare(B.value);case\"number\":return P.valueN,re=te(Q.path).localeCompare(te(oe.path));if(re!=0)return re;let ne=Q.type.localeCompare(oe.type);return ne!=0?ne:Q.subpath&&!oe.subpath?1:!Q.subpath&&oe.subpath?-1:!Q.subpath&&!oe.subpath?0:((G=Q.subpath)!=null?G:\"\").localeCompare((J=oe.subpath)!=null?J:\"\");case\"date\":return P.value0;case\"boolean\":return O.value;case\"link\":return!!O.value.path;case\"date\":return O.value.toMillis()!=0;case\"duration\":return O.value.as(\"seconds\")!=0;case\"object\":return Object.keys(O.value).length>0;case\"array\":return O.value.length>0;case\"null\":return!1;case\"html\":case\"widget\":case\"function\":return!0}}e.isTruthy=o;function s(x){if(x==null)return x;if(e.isArray(x))return[].concat(x.map(O=>s(O)));if(e.isObject(x)){let O={};for(let[A,P]of Object.entries(x))O[A]=s(P);return O}else return x}e.deepCopy=s;function u(x){return typeof x==\"string\"}e.isString=u;function l(x){return typeof x==\"number\"}e.isNumber=l;function c(x){return x instanceof mt}e.isDate=c;function d(x){return x instanceof He}e.isDuration=d;function m(x){return x==null}e.isNull=m;function h(x){return Array.isArray(x)}e.isArray=h;function g(x){return typeof x==\"boolean\"}e.isBoolean=g;function y(x){return x instanceof xc}e.isLink=y;function v(x){return x instanceof Tl}e.isWidget=v;function D(x){return typeof HTMLElement!=\"undefined\"?x instanceof HTMLElement:!1}e.isHtml=D;function I(x){return typeof x==\"object\"&&!D(x)&&!v(x)&&!h(x)&&!d(x)&&!c(x)&&!y(x)&&x!==void 0&&!m(x)}e.isObject=I;function C(x){return typeof x==\"function\"}e.isFunction=C})(Ml||(Ml={}));var L0;(function(e){function t(i){return Ml.isObject(i)&&Object.keys(i).length==2&&\"key\"in i&&\"rows\"in i}e.isElementGroup=t;function r(i){for(let a of i)if(!t(a))return!1;return!0}e.isGrouping=r;function n(i){if(r(i)){let a=0;for(let o of i)a+=n(o.rows);return a}else return i.length}e.count=n})(L0||(L0={}));var xc=class e{constructor(t){ur(this,\"path\");ur(this,\"display\");ur(this,\"subpath\");ur(this,\"embed\");ur(this,\"type\");Object.assign(this,t)}static file(t,r=!1,n){return new e({path:t,embed:r,display:n,subpath:void 0,type:\"file\"})}static infer(t,r=!1,n){if(t.includes(\"#^\")){let i=t.split(\"#^\");return e.block(i[0],i[1],r,n)}else if(t.includes(\"#\")){let i=t.split(\"#\");return e.header(i[0],i[1],r,n)}else return e.file(t,r,n)}static header(t,r,n,i){return new e({path:t,embed:n,display:i,subpath:$P(r),type:\"header\"})}static block(t,r,n,i){return new e({path:t,embed:n,display:i,subpath:r,type:\"block\"})}static fromObject(t){return new e(t)}equals(t){return t==null||t==null?!1:this.path==t.path&&this.type==t.type&&this.subpath==t.subpath}toString(){return this.markdown()}toObject(){return{path:this.path,type:this.type,subpath:this.subpath,display:this.display,embed:this.embed}}withPath(t){return new e(Object.assign({},this,{path:t}))}withDisplay(t){return new e(Object.assign({},this,{display:t}))}withHeader(t){return e.header(this.path,t,this.embed,this.display)}toFile(){return e.file(this.path,this.embed,this.display)}toEmbed(){if(this.embed)return this;{let t=new e(this);return t.embed=!0,t}}fromEmbed(){if(this.embed){let t=new e(this);return t.embed=!1,t}else return this}markdown(){let t=(this.embed?\"!\":\"\")+\"[[\"+this.obsidianLink();return this.display?t+=\"|\"+this.display:(t+=\"|\"+O0(this.path),(this.type==\"header\"||this.type==\"block\")&&(t+=\" > \"+this.subpath)),t+=\"]]\",t}obsidianLink(){var r,n;let t=this.path.replaceAll(\"|\",\"\\\\|\");return this.type==\"header\"?t+\"#\"+((r=this.subpath)==null?void 0:r.replaceAll(\"|\",\"\\\\|\")):this.type==\"block\"?t+\"#^\"+((n=this.subpath)==null?void 0:n.replaceAll(\"|\",\"\\\\|\")):t}fileName(){return O0(this.path).replace(\".md\",\"\")}},Tl=class{constructor(t){ur(this,\"$widget\");this.$widget=t}},_m=class extends Tl{constructor(r,n){super(\"dataview:list-pair\");ur(this,\"key\");ur(this,\"value\");this.key=r,this.value=n}markdown(){return`${Ml.toString(this.key)}: ${Ml.toString(this.value)}`}},Mm=class extends Tl{constructor(r,n){super(\"dataview:external-link\");ur(this,\"url\");ur(this,\"display\");this.url=r,this.display=n}markdown(){var r;return`[${(r=this.display)!=null?r:this.url}](${this.url})`}},P0;(function(e){function t(o,s){return new _m(o,s)}e.listPair=t;function r(o,s){return new Mm(o,s)}e.externalLink=r;function n(o){return o.$widget===\"dataview:list-pair\"}e.isListPair=n;function i(o){return o.$widget===\"dataview:external-link\"}e.isExternalLink=i;function a(o){return n(o)||i(o)}e.isBuiltin=a})(P0||(P0={}));var fn;(function(e){function t(m){return{type:\"variable\",name:m}}e.variable=t;function r(m){return{type:\"literal\",value:m}}e.literal=r;function n(m,h,g){return{type:\"binaryop\",left:m,op:h,right:g}}e.binaryOp=n;function i(m,h){return{type:\"index\",object:m,index:h}}e.index=i;function a(m){let h=m.split(\".\"),g=e.variable(h[0]);for(let y=1;y\"||m==\">=\"||m==\"!=\"||m==\"=\"}e.isCompareOp=d,e.NULL=e.literal(null)})(fn||(fn={}));var Si;(function(e){function t(c){return{type:\"tag\",tag:c}}e.tag=t;function r(c){return{type:\"csv\",path:c}}e.csv=r;function n(c){return{type:\"folder\",folder:c}}e.folder=n;function i(c,d){return{type:\"link\",file:c,direction:d?\"incoming\":\"outgoing\"}}e.link=i;function a(c,d,m){return{type:\"binaryop\",left:c,op:d,right:m}}e.binaryOp=a;function o(c,d){return{type:\"binaryop\",left:c,op:\"&\",right:d}}e.and=o;function s(c,d){return{type:\"binaryop\",left:c,op:\"|\",right:d}}e.or=s;function u(c){return{type:\"negate\",child:c}}e.negate=u;function l(){return{type:\"empty\"}}e.empty=l})(Si||(Si={}));var N0=new RegExp(Bm(),\"\"),Tm={year:He.fromObject({years:1}),years:He.fromObject({years:1}),yr:He.fromObject({years:1}),yrs:He.fromObject({years:1}),month:He.fromObject({months:1}),months:He.fromObject({months:1}),mo:He.fromObject({months:1}),mos:He.fromObject({months:1}),week:He.fromObject({weeks:1}),weeks:He.fromObject({weeks:1}),wk:He.fromObject({weeks:1}),wks:He.fromObject({weeks:1}),w:He.fromObject({weeks:1}),day:He.fromObject({days:1}),days:He.fromObject({days:1}),d:He.fromObject({days:1}),hour:He.fromObject({hours:1}),hours:He.fromObject({hours:1}),hr:He.fromObject({hours:1}),hrs:He.fromObject({hours:1}),h:He.fromObject({hours:1}),minute:He.fromObject({minutes:1}),minutes:He.fromObject({minutes:1}),min:He.fromObject({minutes:1}),mins:He.fromObject({minutes:1}),m:He.fromObject({minutes:1}),second:He.fromObject({seconds:1}),seconds:He.fromObject({seconds:1}),sec:He.fromObject({seconds:1}),secs:He.fromObject({seconds:1}),s:He.fromObject({seconds:1})},Fm={now:()=>mt.local(),today:()=>mt.local().startOf(\"day\"),yesterday:()=>mt.local().startOf(\"day\").minus(He.fromObject({days:1})),tomorrow:()=>mt.local().startOf(\"day\").plus(He.fromObject({days:1})),sow:()=>mt.local().startOf(\"week\"),\"start-of-week\":()=>mt.local().startOf(\"week\"),eow:()=>mt.local().endOf(\"week\"),\"end-of-week\":()=>mt.local().endOf(\"week\"),soy:()=>mt.local().startOf(\"year\"),\"start-of-year\":()=>mt.local().startOf(\"year\"),eoy:()=>mt.local().endOf(\"year\"),\"end-of-year\":()=>mt.local().endOf(\"year\"),som:()=>mt.local().startOf(\"month\"),\"start-of-month\":()=>mt.local().startOf(\"month\"),eom:()=>mt.local().endOf(\"month\"),\"end-of-month\":()=>mt.local().endOf(\"month\")},Im=[\"FROM\",\"WHERE\",\"LIMIT\",\"GROUP\",\"FLATTEN\"];function WP(e){let t=-1;for(;(t=e.indexOf(\"|\",t+1))>=0;)if(!(t>0&&e[t-1]==\"\\\\\"))return[e.substring(0,t).replace(/\\\\\\|/g,\"|\"),e.substring(t+1)];return[e.replace(/\\\\\\|/g,\"|\"),void 0]}function YP(e){let[t,r]=WP(e);return xc.infer(t,!1,r)}function kl(e,t,r){return W.seqMap(e,W.seq(W.optWhitespace,t,W.optWhitespace,e).many(),(n,i)=>{if(i.length==0)return n;let a=r(n,i[0][1],i[0][3]);for(let o=1;o(i,a)=>{let o=e._(i,a);if(!o.status)return o;for(let s of t){let u=s(o.value)._(i,o.index);if(!u.status)return o;o=u}return o})}var ai=W.createLanguage({number:e=>W.regexp(/-?[0-9]+(\\.[0-9]+)?/).map(t=>Number.parseFloat(t)).desc(\"number\"),string:e=>W.string('\"').then(W.alt(e.escapeCharacter,W.noneOf('\"\\\\')).atLeast(0).map(t=>t.join(\"\"))).skip(W.string('\"')).desc(\"string\"),escapeCharacter:e=>W.string(\"\\\\\").then(W.any).map(t=>t==='\"'?'\"':t===\"\\\\\"?\"\\\\\":\"\\\\\"+t),bool:e=>W.regexp(/true|false|True|False/).map(t=>t.toLowerCase()==\"true\").desc(\"boolean ('true' or 'false')\"),tag:e=>W.seqMap(W.string(\"#\"),W.alt(W.regexp(/[^\\u2000-\\u206F\\u2E00-\\u2E7F'!\"#$%&()*+,.:;<=>?@^`{|}~\\[\\]\\\\\\s]/).desc(\"text\")).many(),(t,r)=>t+r.join(\"\")).desc(\"tag ('#hello/stuff')\"),identifier:e=>W.seqMap(W.alt(W.regexp(/\\p{Letter}/u),W.regexp(N0).desc(\"text\")),W.alt(W.regexp(/[0-9\\p{Letter}_-]/u),W.regexp(N0).desc(\"text\")).many(),(t,r)=>t+r.join(\"\")).desc(\"variable identifier\"),link:e=>W.regexp(/\\[\\[([^\\[\\]]*?)\\]\\]/u,1).map(t=>YP(t)).desc(\"file link\"),embedLink:e=>W.seqMap(W.string(\"!\").atMost(1),e.link,(t,r)=>(t.length>0&&(r.embed=!0),r)).desc(\"file link\"),binaryPlusMinus:e=>W.regexp(/\\+|-/).map(t=>t).desc(\"'+' or '-'\"),binaryMulDiv:e=>W.regexp(/\\*|\\/|%/).map(t=>t).desc(\"'*' or '/' or '%'\"),binaryCompareOp:e=>W.regexp(/>=|<=|!=|>|<|=/).map(t=>t).desc(\"'>=' or '<=' or '!=' or '=' or '>' or '<'\"),binaryBooleanOp:e=>W.regexp(/and|or|&|\\|/i).map(t=>t.toLowerCase()==\"and\"?\"&\":t.toLowerCase()==\"or\"?\"|\":t).desc(\"'and' or 'or'\"),rootDate:e=>W.seqMap(W.regexp(/\\d{4}/),W.string(\"-\"),W.regexp(/\\d{2}/),(t,r,n)=>mt.fromObject({year:Number.parseInt(t),month:Number.parseInt(n)})).desc(\"date in format YYYY-MM[-DDTHH-MM-SS.MS]\"),dateShorthand:e=>W.alt(...Object.keys(Fm).sort((t,r)=>r.length-t.length).map(W.string)),date:e=>zP(e.rootDate,t=>W.seqMap(W.string(\"-\"),W.regexp(/\\d{2}/),(r,n)=>t.set({day:Number.parseInt(n)})),t=>W.seqMap(W.string(\"T\"),W.regexp(/\\d{2}/),(r,n)=>t.set({hour:Number.parseInt(n)})),t=>W.seqMap(W.string(\":\"),W.regexp(/\\d{2}/),(r,n)=>t.set({minute:Number.parseInt(n)})),t=>W.seqMap(W.string(\":\"),W.regexp(/\\d{2}/),(r,n)=>t.set({second:Number.parseInt(n)})),t=>W.alt(W.seqMap(W.string(\".\"),W.regexp(/\\d{3}/),(r,n)=>t.set({millisecond:Number.parseInt(n)})),W.succeed(t)),t=>W.alt(W.seqMap(W.string(\"+\").or(W.string(\"-\")),W.regexp(/\\d{1,2}(:\\d{2})?/),(r,n)=>t.setZone(\"UTC\"+r+n,{keepLocalTime:!0})),W.seqMap(W.string(\"Z\"),()=>t.setZone(\"utc\",{keepLocalTime:!0})),W.seqMap(W.string(\"[\"),W.regexp(/[0-9A-Za-z+-\\/]+/u),W.string(\"]\"),(r,n,i)=>t.setZone(n,{keepLocalTime:!0})))).assert(t=>t.isValid,\"valid date\").desc(\"date in format YYYY-MM[-DDTHH-MM-SS.MS]\"),datePlus:e=>W.alt(e.dateShorthand.map(t=>Fm[t]()),e.date).desc(\"date in format YYYY-MM[-DDTHH-MM-SS.MS] or in shorthand\"),durationType:e=>W.alt(...Object.keys(Tm).sort((t,r)=>r.length-t.length).map(W.string)),duration:e=>W.seqMap(e.number,W.optWhitespace,e.durationType,(t,r,n)=>Tm[n].mapUnits(i=>i*t)).sepBy1(W.string(\",\").trim(W.optWhitespace).or(W.optWhitespace)).map(t=>t.reduce((r,n)=>r.plus(n))).desc(\"duration like 4hr2min\"),rawNull:e=>W.string(\"null\"),tagSource:e=>e.tag.map(t=>Si.tag(t)),csvSource:e=>W.seqMap(W.string(\"csv(\").skip(W.optWhitespace),e.string,W.string(\")\"),(t,r,n)=>Si.csv(r)),linkIncomingSource:e=>e.link.map(t=>Si.link(t.path,!0)),linkOutgoingSource:e=>W.seqMap(W.string(\"outgoing(\").skip(W.optWhitespace),e.link,W.string(\")\"),(t,r,n)=>Si.link(r.path,!1)),folderSource:e=>e.string.map(t=>Si.folder(t)),parensSource:e=>W.seqMap(W.string(\"(\"),W.optWhitespace,e.source,W.optWhitespace,W.string(\")\"),(t,r,n,i,a)=>n),negateSource:e=>W.seqMap(W.alt(W.string(\"-\"),W.string(\"!\")),e.atomSource,(t,r)=>Si.negate(r)),atomSource:e=>W.alt(e.parensSource,e.negateSource,e.linkOutgoingSource,e.linkIncomingSource,e.folderSource,e.tagSource,e.csvSource),binaryOpSource:e=>kl(e.atomSource,e.binaryBooleanOp.map(t=>t),Si.binaryOp),source:e=>e.binaryOpSource,variableField:e=>e.identifier.chain(t=>Im.includes(t.toUpperCase())?W.fail(\"Variable fields cannot be a keyword (\"+Im.join(\" or \")+\")\"):W.succeed(fn.variable(t))).desc(\"variable\"),numberField:e=>e.number.map(t=>fn.literal(t)).desc(\"number\"),stringField:e=>e.string.map(t=>fn.literal(t)).desc(\"string\"),boolField:e=>e.bool.map(t=>fn.literal(t)).desc(\"boolean\"),dateField:e=>W.seqMap(W.string(\"date(\"),W.optWhitespace,e.datePlus,W.optWhitespace,W.string(\")\"),(t,r,n,i,a)=>fn.literal(n)).desc(\"date\"),durationField:e=>W.seqMap(W.string(\"dur(\"),W.optWhitespace,e.duration,W.optWhitespace,W.string(\")\"),(t,r,n,i,a)=>fn.literal(n)).desc(\"duration\"),nullField:e=>e.rawNull.map(t=>fn.NULL),linkField:e=>e.link.map(t=>fn.literal(t)),listField:e=>e.field.sepBy(W.string(\",\").trim(W.optWhitespace)).wrap(W.string(\"[\").skip(W.optWhitespace),W.optWhitespace.then(W.string(\"]\"))).map(t=>fn.list(t)).desc(\"list ('[1, 2, 3]')\"),objectField:e=>W.seqMap(e.identifier.or(e.string),W.string(\":\").trim(W.optWhitespace),e.field,(t,r,n)=>({name:t,value:n})).sepBy(W.string(\",\").trim(W.optWhitespace)).wrap(W.string(\"{\").skip(W.optWhitespace),W.optWhitespace.then(W.string(\"}\"))).map(t=>{let r={};for(let n of t)r[n.name]=n.value;return fn.object(r)}).desc(\"object ('{ a: 1, b: 2 }')\"),atomInlineField:e=>W.alt(e.date,e.duration.map(t=>NS(t)),e.string,e.tag,e.embedLink,e.bool,e.number,e.rawNull),inlineFieldList:e=>e.atomInlineField.sepBy(W.string(\",\").trim(W.optWhitespace).lookahead(e.atomInlineField)),inlineField:e=>W.alt(W.seqMap(e.atomInlineField,W.string(\",\").trim(W.optWhitespace),e.inlineFieldList,(t,r,n)=>[t].concat(n)),e.atomInlineField),atomField:e=>W.alt(e.embedLink.map(t=>fn.literal(t)),e.negatedField,e.linkField,e.listField,e.objectField,e.lambdaField,e.parensField,e.boolField,e.numberField,e.stringField,e.dateField,e.durationField,e.nullField,e.variableField),indexField:e=>W.seqMap(e.atomField,W.alt(e.dotPostfix,e.indexPostfix,e.functionPostfix).many(),(t,r)=>{let n=t;for(let i of r)switch(i.type){case\"dot\":n=fn.index(n,fn.literal(i.field));break;case\"index\":n=fn.index(n,i.field);break;case\"function\":n=fn.func(n,i.fields);break}return n}),negatedField:e=>W.seqMap(W.string(\"!\"),e.indexField,(t,r)=>fn.negate(r)).desc(\"negated field\"),parensField:e=>W.seqMap(W.string(\"(\"),W.optWhitespace,e.field,W.optWhitespace,W.string(\")\"),(t,r,n,i,a)=>n),lambdaField:e=>W.seqMap(e.identifier.sepBy(W.string(\",\").trim(W.optWhitespace)).wrap(W.string(\"(\").trim(W.optWhitespace),W.string(\")\").trim(W.optWhitespace)),W.string(\"=>\").trim(W.optWhitespace),e.field,(t,r,n)=>({type:\"lambda\",arguments:t,value:n})),dotPostfix:e=>W.seqMap(W.string(\".\"),e.identifier,(t,r)=>({type:\"dot\",field:r})),indexPostfix:e=>W.seqMap(W.string(\"[\"),W.optWhitespace,e.field,W.optWhitespace,W.string(\"]\"),(t,r,n,i,a)=>({type:\"index\",field:n})),functionPostfix:e=>W.seqMap(W.string(\"(\"),W.optWhitespace,e.field.sepBy(W.string(\",\").trim(W.optWhitespace)),W.optWhitespace,W.string(\")\"),(t,r,n,i,a)=>({type:\"function\",fields:n})),binaryMulDivField:e=>kl(e.indexField,e.binaryMulDiv,fn.binaryOp),binaryPlusMinusField:e=>kl(e.binaryMulDivField,e.binaryPlusMinus,fn.binaryOp),binaryCompareField:e=>kl(e.binaryPlusMinusField,e.binaryCompareOp,fn.binaryOp),binaryBooleanField:e=>kl(e.binaryCompareField,e.binaryBooleanOp,fn.binaryOp),binaryOpField:e=>e.binaryBooleanField,field:e=>e.binaryOpField});function KP(e){try{return Ec.success(ai.field.tryParse(e))}catch(t){return Ec.failure(\"\"+t)}}var Cc;(function(e){function t(n,i){return{name:n,field:i}}e.named=t;function r(n,i){return{field:n,direction:i}}e.sortBy=r})(Cc||(Cc={}));function jP(e){return W.custom((t,r)=>(n,i)=>{let a=e._(n,i);return a.status?Object.assign({},a,{value:[a.value,n.substring(i,a.index)]}):a})}function qP(e){return e.split(/[\\r\\n]+/).map(t=>t.trim()).join(\"\")}function R0(e,t){return W.eof.map(e).or(W.whitespace.then(t))}var RS=W.createLanguage({queryType:e=>W.alt(W.regexp(/TABLE|LIST|TASK|CALENDAR/i)).map(t=>t.toLowerCase()).desc(\"query type ('TABLE', 'LIST', 'TASK', or 'CALENDAR')\"),explicitNamedField:e=>W.seqMap(ai.field.skip(W.whitespace),W.regexp(/AS/i).skip(W.whitespace),ai.identifier.or(ai.string),(t,r,n)=>Cc.named(n,t)),comment:()=>W.Parser((e,t)=>{let r=e.substring(t);if(!r.startsWith(\"//\"))return W.makeFailure(t,\"Not a comment\");r=r.split(`\n`)[0];let n=r.substring(2).trim();return W.makeSuccess(t+r.length,n)}),namedField:e=>W.alt(e.explicitNamedField,jP(ai.field).map(([t,r])=>Cc.named(qP(r),t))),sortField:e=>W.seqMap(ai.field.skip(W.optWhitespace),W.regexp(/ASCENDING|DESCENDING|ASC|DESC/i).atMost(1),(t,r)=>{let n=r.length==0?\"ascending\":r[0].toLowerCase();return n==\"desc\"&&(n=\"descending\"),n==\"asc\"&&(n=\"ascending\"),{field:t,direction:n}}),headerClause:e=>e.queryType.chain(t=>{switch(t){case\"table\":return R0(()=>({type:t,fields:[],showId:!0}),W.seqMap(W.regexp(/WITHOUT\\s+ID/i).skip(W.optWhitespace).atMost(1),W.sepBy(e.namedField,W.string(\",\").trim(W.optWhitespace)),(r,n)=>({type:t,fields:n,showId:r.length==0})));case\"list\":return R0(()=>({type:t,format:void 0,showId:!0}),W.seqMap(W.regexp(/WITHOUT\\s+ID/i).skip(W.optWhitespace).atMost(1),ai.field.atMost(1),(r,n)=>({type:t,format:n.length==1?n[0]:void 0,showId:r.length==0})));case\"task\":return W.succeed({type:t});case\"calendar\":return W.whitespace.then(W.seqMap(e.namedField,r=>({type:t,showId:!0,field:r})));default:return W.fail(`Unrecognized query type '${t}'`)}}).desc(\"TABLE or LIST or TASK or CALENDAR\"),fromClause:e=>W.seqMap(W.regexp(/FROM/i),W.whitespace,ai.source,(t,r,n)=>n),whereClause:e=>W.seqMap(W.regexp(/WHERE/i),W.whitespace,ai.field,(t,r,n)=>({type:\"where\",clause:n})).desc(\"WHERE \"),sortByClause:e=>W.seqMap(W.regexp(/SORT/i),W.whitespace,e.sortField.sepBy1(W.string(\",\").trim(W.optWhitespace)),(t,r,n)=>({type:\"sort\",fields:n})).desc(\"SORT field [ASC/DESC]\"),limitClause:e=>W.seqMap(W.regexp(/LIMIT/i),W.whitespace,ai.field,(t,r,n)=>({type:\"limit\",amount:n})).desc(\"LIMIT \"),flattenClause:e=>W.seqMap(W.regexp(/FLATTEN/i).skip(W.whitespace),e.namedField,(t,r)=>({type:\"flatten\",field:r})).desc(\"FLATTEN [AS ]\"),groupByClause:e=>W.seqMap(W.regexp(/GROUP BY/i).skip(W.whitespace),e.namedField,(t,r)=>({type:\"group\",field:r})).desc(\"GROUP BY [AS ]\"),clause:e=>W.alt(e.fromClause,e.whereClause,e.sortByClause,e.limitClause,e.groupByClause,e.flattenClause),query:e=>W.seqMap(e.headerClause.trim(dm),e.fromClause.trim(dm).atMost(1),e.clause.trim(dm).many(),(t,r,n)=>({header:t,source:r.length==0?Si.folder(\"\"):r[0],operations:n,settings:Hm}))}),dm=W.alt(W.whitespace,RS.comment).many().map(e=>e.join(\"\")),GP=e=>{var t;return e?(t=e.plugins.plugins.dataview)==null?void 0:t.api:window.DataviewAPI},JP=e=>e.plugins.enabledPlugins.has(\"dataview\");Ei.DATE_SHORTHANDS=Fm;Ei.DURATION_TYPES=Tm;Ei.EXPRESSION=ai;Ei.KEYWORDS=Im;Ei.QUERY_LANGUAGE=RS;Ei.getAPI=GP;Ei.isPluginEnabled=JP;Ei.parseField=KP});var HS=wn((Vm,$m)=>{(function(e,t){typeof Vm==\"object\"&&typeof $m!=\"undefined\"?$m.exports=t():typeof define==\"function\"&&define.amd?define(t):e.Mark=t()})(Vm,function(){\"use strict\";var e=typeof Symbol==\"function\"&&typeof Symbol.iterator==\"symbol\"?function(s){return typeof s}:function(s){return s&&typeof Symbol==\"function\"&&s.constructor===Symbol&&s!==Symbol.prototype?\"symbol\":typeof s},t=function(s,u){if(!(s instanceof u))throw new TypeError(\"Cannot call a class as a function\")},r=function(){function s(u,l){for(var c=0;c1&&arguments[1]!==void 0?arguments[1]:!0,c=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[],d=arguments.length>3&&arguments[3]!==void 0?arguments[3]:5e3;t(this,s),this.ctx=u,this.iframes=l,this.exclude=c,this.iframesTimeout=d}return r(s,[{key:\"getContexts\",value:function(){var l=void 0,c=[];return typeof this.ctx==\"undefined\"||!this.ctx?l=[]:NodeList.prototype.isPrototypeOf(this.ctx)?l=Array.prototype.slice.call(this.ctx):Array.isArray(this.ctx)?l=this.ctx:typeof this.ctx==\"string\"?l=Array.prototype.slice.call(document.querySelectorAll(this.ctx)):l=[this.ctx],l.forEach(function(d){var m=c.filter(function(h){return h.contains(d)}).length>0;c.indexOf(d)===-1&&!m&&c.push(d)}),c}},{key:\"getIframeContents\",value:function(l,c){var d=arguments.length>2&&arguments[2]!==void 0?arguments[2]:function(){},m=void 0;try{var h=l.contentWindow;if(m=h.document,!h||!m)throw new Error(\"iframe inaccessible\")}catch(g){d()}m&&c(m)}},{key:\"isIframeBlank\",value:function(l){var c=\"about:blank\",d=l.getAttribute(\"src\").trim(),m=l.contentWindow.location.href;return m===c&&d!==c&&d}},{key:\"observeIframeLoad\",value:function(l,c,d){var m=this,h=!1,g=null,y=function v(){if(!h){h=!0,activeWindow.clearTimeout(g);try{m.isIframeBlank(l)||(l.removeEventListener(\"load\",v),m.getIframeContents(l,c,d))}catch(D){d()}}};l.addEventListener(\"load\",y),g=activeWindow.setTimeout(y,this.iframesTimeout)}},{key:\"onIframeReady\",value:function(l,c,d){try{l.contentWindow.document.readyState===\"complete\"?this.isIframeBlank(l)?this.observeIframeLoad(l,c,d):this.getIframeContents(l,c,d):this.observeIframeLoad(l,c,d)}catch(m){d()}}},{key:\"waitForIframes\",value:function(l,c){var d=this,m=0;this.forEachIframe(l,function(){return!0},function(h){m++,d.waitForIframes(h.querySelector(\"html\"),function(){--m||c()})},function(h){h||c()})}},{key:\"forEachIframe\",value:function(l,c,d){var m=this,h=arguments.length>3&&arguments[3]!==void 0?arguments[3]:function(){},g=l.querySelectorAll(\"iframe\"),y=g.length,v=0;g=Array.prototype.slice.call(g);var D=function(){--y<=0&&h(v)};y||D(),g.forEach(function(I){s.matches(I,m.exclude)?D():m.onIframeReady(I,function(C){c(I)&&(v++,d(C)),D()},D)})}},{key:\"createIterator\",value:function(l,c,d){return document.createNodeIterator(l,c,d,!1)}},{key:\"createInstanceOnIframe\",value:function(l){return new s(l.querySelector(\"html\"),this.iframes)}},{key:\"compareNodeIframe\",value:function(l,c,d){var m=l.compareDocumentPosition(d),h=Node.DOCUMENT_POSITION_PRECEDING;if(m&h)if(c!==null){var g=c.compareDocumentPosition(d),y=Node.DOCUMENT_POSITION_FOLLOWING;if(g&y)return!0}else return!0;return!1}},{key:\"getIteratorNode\",value:function(l){var c=l.previousNode(),d=void 0;return c===null?d=l.nextNode():d=l.nextNode()&&l.nextNode(),{prevNode:c,node:d}}},{key:\"checkIframeFilter\",value:function(l,c,d,m){var h=!1,g=!1;return m.forEach(function(y,v){y.val===d&&(h=v,g=y.handled)}),this.compareNodeIframe(l,c,d)?(h===!1&&!g?m.push({val:d,handled:!0}):h!==!1&&!g&&(m[h].handled=!0),!0):(h===!1&&m.push({val:d,handled:!1}),!1)}},{key:\"handleOpenIframes\",value:function(l,c,d,m){var h=this;l.forEach(function(g){g.handled||h.getIframeContents(g.val,function(y){h.createInstanceOnIframe(y).forEachNode(c,d,m)})})}},{key:\"iterateThroughNodes\",value:function(l,c,d,m,h){for(var g=this,y=this.createIterator(c,l,m),v=[],D=[],I=void 0,C=void 0,x=function(){var A=g.getIteratorNode(y);return C=A.prevNode,I=A.node,I};x();)this.iframes&&this.forEachIframe(c,function(O){return g.checkIframeFilter(I,C,O,v)},function(O){g.createInstanceOnIframe(O).forEachNode(l,function(A){return D.push(A)},m)}),D.push(I);D.forEach(function(O){d(O)}),this.iframes&&this.handleOpenIframes(v,l,d,m),h()}},{key:\"forEachNode\",value:function(l,c,d){var m=this,h=arguments.length>3&&arguments[3]!==void 0?arguments[3]:function(){},g=this.getContexts(),y=g.length;y||h(),g.forEach(function(v){var D=function(){m.iterateThroughNodes(l,v,c,d,function(){--y<=0&&h()})};m.iframes?m.waitForIframes(v,D):D()})}}],[{key:\"matches\",value:function(l,c){var d=typeof c==\"string\"?[c]:c,m=l.matches||l.matchesSelector||l.msMatchesSelector||l.mozMatchesSelector||l.oMatchesSelector||l.webkitMatchesSelector;if(m){var h=!1;return d.every(function(g){return m.call(l,g)?(h=!0,!1):!0}),h}else return!1}}]),s}(),a=function(){function s(u){t(this,s),this.ctx=u,this.ie=!1;var l=window.navigator.userAgent;(l.indexOf(\"MSIE\")>-1||l.indexOf(\"Trident\")>-1)&&(this.ie=!0)}return r(s,[{key:\"log\",value:function(l){var c=arguments.length>1&&arguments[1]!==void 0?arguments[1]:\"debug\",d=this.opt.log;this.opt.debug&&(typeof d==\"undefined\"?\"undefined\":e(d))===\"object\"&&typeof d[c]==\"function\"&&d[c](\"mark.js: \"+l)}},{key:\"escapeStr\",value:function(l){return l.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g,\"\\\\$&\")}},{key:\"createRegExp\",value:function(l){return this.opt.wildcards!==\"disabled\"&&(l=this.setupWildcardsRegExp(l)),l=this.escapeStr(l),Object.keys(this.opt.synonyms).length&&(l=this.createSynonymsRegExp(l)),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(l=this.setupIgnoreJoinersRegExp(l)),this.opt.diacritics&&(l=this.createDiacriticsRegExp(l)),l=this.createMergedBlanksRegExp(l),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(l=this.createJoinersRegExp(l)),this.opt.wildcards!==\"disabled\"&&(l=this.createWildcardsRegExp(l)),l=this.createAccuracyRegExp(l),l}},{key:\"createSynonymsRegExp\",value:function(l){var c=this.opt.synonyms,d=this.opt.caseSensitive?\"\":\"i\",m=this.opt.ignoreJoiners||this.opt.ignorePunctuation.length?\"\\0\":\"\";for(var h in c)if(c.hasOwnProperty(h)){var g=c[h],y=this.opt.wildcards!==\"disabled\"?this.setupWildcardsRegExp(h):this.escapeStr(h),v=this.opt.wildcards!==\"disabled\"?this.setupWildcardsRegExp(g):this.escapeStr(g);y!==\"\"&&v!==\"\"&&(l=l.replace(new RegExp(\"(\"+this.escapeStr(y)+\"|\"+this.escapeStr(v)+\")\",\"gm\"+d),m+(\"(\"+this.processSynomyms(y)+\"|\")+(this.processSynomyms(v)+\")\")+m))}return l}},{key:\"processSynomyms\",value:function(l){return(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(l=this.setupIgnoreJoinersRegExp(l)),l}},{key:\"setupWildcardsRegExp\",value:function(l){return l=l.replace(/(?:\\\\)*\\?/g,function(c){return c.charAt(0)===\"\\\\\"?\"?\":\"\u0001\"}),l.replace(/(?:\\\\)*\\*/g,function(c){return c.charAt(0)===\"\\\\\"?\"*\":\"\u0002\"})}},{key:\"createWildcardsRegExp\",value:function(l){var c=this.opt.wildcards===\"withSpaces\";return l.replace(/\\u0001/g,c?\"[\\\\S\\\\s]?\":\"\\\\S?\").replace(/\\u0002/g,c?\"[\\\\S\\\\s]*?\":\"\\\\S*\")}},{key:\"setupIgnoreJoinersRegExp\",value:function(l){return l.replace(/[^(|)\\\\]/g,function(c,d,m){var h=m.charAt(d+1);return/[(|)\\\\]/.test(h)||h===\"\"?c:c+\"\\0\"})}},{key:\"createJoinersRegExp\",value:function(l){var c=[],d=this.opt.ignorePunctuation;return Array.isArray(d)&&d.length&&c.push(this.escapeStr(d.join(\"\"))),this.opt.ignoreJoiners&&c.push(\"\\\\u00ad\\\\u200b\\\\u200c\\\\u200d\"),c.length?l.split(/\\u0000+/).join(\"[\"+c.join(\"\")+\"]*\"):l}},{key:\"createDiacriticsRegExp\",value:function(l){var c=this.opt.caseSensitive?\"\":\"i\",d=this.opt.caseSensitive?[\"a\\xE0\\xE1\\u1EA3\\xE3\\u1EA1\\u0103\\u1EB1\\u1EAF\\u1EB3\\u1EB5\\u1EB7\\xE2\\u1EA7\\u1EA5\\u1EA9\\u1EAB\\u1EAD\\xE4\\xE5\\u0101\\u0105\",\"A\\xC0\\xC1\\u1EA2\\xC3\\u1EA0\\u0102\\u1EB0\\u1EAE\\u1EB2\\u1EB4\\u1EB6\\xC2\\u1EA6\\u1EA4\\u1EA8\\u1EAA\\u1EAC\\xC4\\xC5\\u0100\\u0104\",\"c\\xE7\\u0107\\u010D\",\"C\\xC7\\u0106\\u010C\",\"d\\u0111\\u010F\",\"D\\u0110\\u010E\",\"e\\xE8\\xE9\\u1EBB\\u1EBD\\u1EB9\\xEA\\u1EC1\\u1EBF\\u1EC3\\u1EC5\\u1EC7\\xEB\\u011B\\u0113\\u0119\",\"E\\xC8\\xC9\\u1EBA\\u1EBC\\u1EB8\\xCA\\u1EC0\\u1EBE\\u1EC2\\u1EC4\\u1EC6\\xCB\\u011A\\u0112\\u0118\",\"i\\xEC\\xED\\u1EC9\\u0129\\u1ECB\\xEE\\xEF\\u012B\",\"I\\xCC\\xCD\\u1EC8\\u0128\\u1ECA\\xCE\\xCF\\u012A\",\"l\\u0142\",\"L\\u0141\",\"n\\xF1\\u0148\\u0144\",\"N\\xD1\\u0147\\u0143\",\"o\\xF2\\xF3\\u1ECF\\xF5\\u1ECD\\xF4\\u1ED3\\u1ED1\\u1ED5\\u1ED7\\u1ED9\\u01A1\\u1EDF\\u1EE1\\u1EDB\\u1EDD\\u1EE3\\xF6\\xF8\\u014D\",\"O\\xD2\\xD3\\u1ECE\\xD5\\u1ECC\\xD4\\u1ED2\\u1ED0\\u1ED4\\u1ED6\\u1ED8\\u01A0\\u1EDE\\u1EE0\\u1EDA\\u1EDC\\u1EE2\\xD6\\xD8\\u014C\",\"r\\u0159\",\"R\\u0158\",\"s\\u0161\\u015B\\u0219\\u015F\",\"S\\u0160\\u015A\\u0218\\u015E\",\"t\\u0165\\u021B\\u0163\",\"T\\u0164\\u021A\\u0162\",\"u\\xF9\\xFA\\u1EE7\\u0169\\u1EE5\\u01B0\\u1EEB\\u1EE9\\u1EED\\u1EEF\\u1EF1\\xFB\\xFC\\u016F\\u016B\",\"U\\xD9\\xDA\\u1EE6\\u0168\\u1EE4\\u01AF\\u1EEA\\u1EE8\\u1EEC\\u1EEE\\u1EF0\\xDB\\xDC\\u016E\\u016A\",\"y\\xFD\\u1EF3\\u1EF7\\u1EF9\\u1EF5\\xFF\",\"Y\\xDD\\u1EF2\\u1EF6\\u1EF8\\u1EF4\\u0178\",\"z\\u017E\\u017C\\u017A\",\"Z\\u017D\\u017B\\u0179\"]:[\"a\\xE0\\xE1\\u1EA3\\xE3\\u1EA1\\u0103\\u1EB1\\u1EAF\\u1EB3\\u1EB5\\u1EB7\\xE2\\u1EA7\\u1EA5\\u1EA9\\u1EAB\\u1EAD\\xE4\\xE5\\u0101\\u0105A\\xC0\\xC1\\u1EA2\\xC3\\u1EA0\\u0102\\u1EB0\\u1EAE\\u1EB2\\u1EB4\\u1EB6\\xC2\\u1EA6\\u1EA4\\u1EA8\\u1EAA\\u1EAC\\xC4\\xC5\\u0100\\u0104\",\"c\\xE7\\u0107\\u010DC\\xC7\\u0106\\u010C\",\"d\\u0111\\u010FD\\u0110\\u010E\",\"e\\xE8\\xE9\\u1EBB\\u1EBD\\u1EB9\\xEA\\u1EC1\\u1EBF\\u1EC3\\u1EC5\\u1EC7\\xEB\\u011B\\u0113\\u0119E\\xC8\\xC9\\u1EBA\\u1EBC\\u1EB8\\xCA\\u1EC0\\u1EBE\\u1EC2\\u1EC4\\u1EC6\\xCB\\u011A\\u0112\\u0118\",\"i\\xEC\\xED\\u1EC9\\u0129\\u1ECB\\xEE\\xEF\\u012BI\\xCC\\xCD\\u1EC8\\u0128\\u1ECA\\xCE\\xCF\\u012A\",\"l\\u0142L\\u0141\",\"n\\xF1\\u0148\\u0144N\\xD1\\u0147\\u0143\",\"o\\xF2\\xF3\\u1ECF\\xF5\\u1ECD\\xF4\\u1ED3\\u1ED1\\u1ED5\\u1ED7\\u1ED9\\u01A1\\u1EDF\\u1EE1\\u1EDB\\u1EDD\\u1EE3\\xF6\\xF8\\u014DO\\xD2\\xD3\\u1ECE\\xD5\\u1ECC\\xD4\\u1ED2\\u1ED0\\u1ED4\\u1ED6\\u1ED8\\u01A0\\u1EDE\\u1EE0\\u1EDA\\u1EDC\\u1EE2\\xD6\\xD8\\u014C\",\"r\\u0159R\\u0158\",\"s\\u0161\\u015B\\u0219\\u015FS\\u0160\\u015A\\u0218\\u015E\",\"t\\u0165\\u021B\\u0163T\\u0164\\u021A\\u0162\",\"u\\xF9\\xFA\\u1EE7\\u0169\\u1EE5\\u01B0\\u1EEB\\u1EE9\\u1EED\\u1EEF\\u1EF1\\xFB\\xFC\\u016F\\u016BU\\xD9\\xDA\\u1EE6\\u0168\\u1EE4\\u01AF\\u1EEA\\u1EE8\\u1EEC\\u1EEE\\u1EF0\\xDB\\xDC\\u016E\\u016A\",\"y\\xFD\\u1EF3\\u1EF7\\u1EF9\\u1EF5\\xFFY\\xDD\\u1EF2\\u1EF6\\u1EF8\\u1EF4\\u0178\",\"z\\u017E\\u017C\\u017AZ\\u017D\\u017B\\u0179\"],m=[];return l.split(\"\").forEach(function(h){d.every(function(g){if(g.indexOf(h)!==-1){if(m.indexOf(g)>-1)return!1;l=l.replace(new RegExp(\"[\"+g+\"]\",\"gm\"+c),\"[\"+g+\"]\"),m.push(g)}return!0})}),l}},{key:\"createMergedBlanksRegExp\",value:function(l){return l.replace(/[\\s]+/gmi,\"[\\\\s]+\")}},{key:\"createAccuracyRegExp\",value:function(l){var c=this,d=\"!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~\\xA1\\xBF\",m=this.opt.accuracy,h=typeof m==\"string\"?m:m.value,g=typeof m==\"string\"?[]:m.limiters,y=\"\";switch(g.forEach(function(v){y+=\"|\"+c.escapeStr(v)}),h){case\"partially\":default:return\"()(\"+l+\")\";case\"complementary\":return y=\"\\\\s\"+(y||this.escapeStr(d)),\"()([^\"+y+\"]*\"+l+\"[^\"+y+\"]*)\";case\"exactly\":return\"(^|\\\\s\"+y+\")(\"+l+\")(?=$|\\\\s\"+y+\")\"}}},{key:\"getSeparatedKeywords\",value:function(l){var c=this,d=[];return l.forEach(function(m){c.opt.separateWordSearch?m.split(\" \").forEach(function(h){h.trim()&&d.indexOf(h)===-1&&d.push(h)}):m.trim()&&d.indexOf(m)===-1&&d.push(m)}),{keywords:d.sort(function(m,h){return h.length-m.length}),length:d.length}}},{key:\"isNumeric\",value:function(l){return Number(parseFloat(l))==l}},{key:\"checkRanges\",value:function(l){var c=this;if(!Array.isArray(l)||Object.prototype.toString.call(l[0])!==\"[object Object]\")return this.log(\"markRanges() will only accept an array of objects\"),this.opt.noMatch(l),[];var d=[],m=0;return l.sort(function(h,g){return h.start-g.start}).forEach(function(h){var g=c.callNoMatchOnInvalidRanges(h,m),y=g.start,v=g.end,D=g.valid;D&&(h.start=y,h.length=v-y,d.push(h),m=v)}),d}},{key:\"callNoMatchOnInvalidRanges\",value:function(l,c){var d=void 0,m=void 0,h=!1;return l&&typeof l.start!=\"undefined\"?(d=parseInt(l.start,10),m=d+parseInt(l.length,10),this.isNumeric(l.start)&&this.isNumeric(l.length)&&m-c>0&&m-d>0?h=!0:(this.log(\"Ignoring invalid or overlapping range: \"+(\"\"+JSON.stringify(l))),this.opt.noMatch(l))):(this.log(\"Ignoring invalid range: \"+JSON.stringify(l)),this.opt.noMatch(l)),{start:d,end:m,valid:h}}},{key:\"checkWhitespaceRanges\",value:function(l,c,d){var m=void 0,h=!0,g=d.length,y=c-g,v=parseInt(l.start,10)-y;return v=v>g?g:v,m=v+parseInt(l.length,10),m>g&&(m=g,this.log(\"End range automatically set to the max value of \"+g)),v<0||m-v<0||v>g||m>g?(h=!1,this.log(\"Invalid range: \"+JSON.stringify(l)),this.opt.noMatch(l)):d.substring(v,m).replace(/\\s+/g,\"\")===\"\"&&(h=!1,this.log(\"Skipping whitespace only range: \"+JSON.stringify(l)),this.opt.noMatch(l)),{start:v,end:m,valid:h}}},{key:\"getTextNodes\",value:function(l){var c=this,d=\"\",m=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,function(h){m.push({start:d.length,end:(d+=h.textContent).length,node:h})},function(h){return c.matchesExclude(h.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},function(){l({value:d,nodes:m})})}},{key:\"matchesExclude\",value:function(l){return i.matches(l,this.opt.exclude.concat([\"script\",\"style\",\"title\",\"head\",\"html\"]))}},{key:\"wrapRangeInTextNode\",value:function(l,c,d){var m=this.opt.element?this.opt.element:\"mark\",h=l.splitText(c),g=h.splitText(d-c),y=document.createElement(m);return y.setAttribute(\"data-markjs\",\"true\"),this.opt.className&&y.setAttribute(\"class\",this.opt.className),y.textContent=h.textContent,h.parentNode.replaceChild(y,h),g}},{key:\"wrapRangeInMappedTextNode\",value:function(l,c,d,m,h){var g=this;l.nodes.every(function(y,v){var D=l.nodes[v+1];if(typeof D==\"undefined\"||D.start>c){if(!m(y.node))return!1;var I=c-y.start,C=(d>y.end?y.end:d)-y.start,x=l.value.substr(0,y.start),O=l.value.substr(C+y.start);if(y.node=g.wrapRangeInTextNode(y.node,I,C),l.value=x+O,l.nodes.forEach(function(A,P){P>=v&&(l.nodes[P].start>0&&P!==v&&(l.nodes[P].start-=C),l.nodes[P].end-=C)}),d-=C,h(y.node.previousSibling,y.start),d>y.end)c=y.end;else return!1}return!0})}},{key:\"wrapMatches\",value:function(l,c,d,m,h){var g=this,y=c===0?0:c+1;this.getTextNodes(function(v){v.nodes.forEach(function(D){D=D.node;for(var I=void 0;(I=l.exec(D.textContent))!==null&&I[y]!==\"\";)if(d(I[y],D)){var C=I.index;if(y!==0)for(var x=1;x{(function(e,t){typeof Um==\"object\"&&typeof ls!=\"undefined\"?ls.exports=t():typeof define==\"function\"&&define.amd?define(t):e.moment=t()})(Um,function(){\"use strict\";var e;function t(){return e.apply(null,arguments)}function r(f){e=f}function n(f){return f instanceof Array||Object.prototype.toString.call(f)===\"[object Array]\"}function i(f){return f!=null&&Object.prototype.toString.call(f)===\"[object Object]\"}function a(f,p){return Object.prototype.hasOwnProperty.call(f,p)}function o(f){if(Object.getOwnPropertyNames)return Object.getOwnPropertyNames(f).length===0;var p;for(p in f)if(a(f,p))return!1;return!0}function s(f){return f===void 0}function u(f){return typeof f==\"number\"||Object.prototype.toString.call(f)===\"[object Number]\"}function l(f){return f instanceof Date||Object.prototype.toString.call(f)===\"[object Date]\"}function c(f,p){var w=[],S,F=f.length;for(S=0;S>>0,S;for(S=0;S0)for(w=0;w=0;return(j?w?\"+\":\"\":\"-\")+Math.pow(10,Math.max(0,F)).toString().substr(1)+S}var Ce=/(\\[[^\\[]*\\])|(\\\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,U=/(\\[[^\\[]*\\])|(\\\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Je={},it={};function N(f,p,w,S){var F=S;typeof S==\"string\"&&(F=function(){return this[S]()}),f&&(it[f]=F),p&&(it[p[0]]=function(){return De(F.apply(this,arguments),p[1],p[2])}),w&&(it[w]=function(){return this.localeData().ordinal(F.apply(this,arguments),f)})}function Ze(f){return f.match(/\\[[\\s\\S]/)?f.replace(/^\\[|\\]$/g,\"\"):f.replace(/\\\\/g,\"\")}function It(f){var p=f.match(Ce),w,S;for(w=0,S=p.length;w=0&&U.test(f);)f=f.replace(U,S),U.lastIndex=0,w-=1;return f}var vt={LTS:\"h:mm:ss A\",LT:\"h:mm A\",L:\"MM/DD/YYYY\",LL:\"MMMM D, YYYY\",LLL:\"MMMM D, YYYY h:mm A\",LLLL:\"dddd, MMMM D, YYYY h:mm A\"};function Wt(f){var p=this._longDateFormat[f],w=this._longDateFormat[f.toUpperCase()];return p||!w?p:(this._longDateFormat[f]=w.match(Ce).map(function(S){return S===\"MMMM\"||S===\"MM\"||S===\"DD\"||S===\"dddd\"?S.slice(1):S}).join(\"\"),this._longDateFormat[f])}var Tt=\"Invalid date\";function Jt(){return this._invalidDate}var en=\"%d\",gn=/\\d{1,2}/;function yn(f){return this._ordinal.replace(\"%d\",f)}var vn={future:\"in %s\",past:\"%s ago\",s:\"a few seconds\",ss:\"%d seconds\",m:\"a minute\",mm:\"%d minutes\",h:\"an hour\",hh:\"%d hours\",d:\"a day\",dd:\"%d days\",w:\"a week\",ww:\"%d weeks\",M:\"a month\",MM:\"%d months\",y:\"a year\",yy:\"%d years\"};function xn(f,p,w,S){var F=this._relativeTime[w];return Q(F)?F(f,p,w,S):F.replace(/%d/i,f)}function jn(f,p){var w=this._relativeTime[f>0?\"future\":\"past\"];return Q(w)?w(p):w.replace(/%s/i,p)}var sn={};function Rt(f,p){var w=f.toLowerCase();sn[w]=sn[w+\"s\"]=sn[p]=f}function Vt(f){return typeof f==\"string\"?sn[f]||sn[f.toLowerCase()]:void 0}function Sr(f){var p={},w,S;for(S in f)a(f,S)&&(w=Vt(S),w&&(p[w]=f[S]));return p}var Rr={};function Bt(f,p){Rr[f]=p}function Hr(f){var p=[],w;for(w in f)a(f,w)&&p.push({unit:w,priority:Rr[w]});return p.sort(function(S,F){return S.priority-F.priority}),p}function rn(f){return f%4===0&&f%100!==0||f%400===0}function At(f){return f<0?Math.ceil(f)||0:Math.floor(f)}function Z(f){var p=+f,w=0;return p!==0&&isFinite(p)&&(w=At(p)),w}function me(f,p){return function(w){return w!=null?(at(this,f,w),t.updateOffset(this,p),this):Ie(this,f)}}function Ie(f,p){return f.isValid()?f._d[\"get\"+(f._isUTC?\"UTC\":\"\")+p]():NaN}function at(f,p,w){f.isValid()&&!isNaN(w)&&(p===\"FullYear\"&&rn(f.year())&&f.month()===1&&f.date()===29?(w=Z(w),f._d[\"set\"+(f._isUTC?\"UTC\":\"\")+p](w,f.month(),b(w,f.month()))):f._d[\"set\"+(f._isUTC?\"UTC\":\"\")+p](w))}function Dt(f){return f=Vt(f),Q(this[f])?this[f]():this}function Cn(f,p){if(typeof f==\"object\"){f=Sr(f);var w=Hr(f),S,F=w.length;for(S=0;S68?1900:2e3)};var On=me(\"FullYear\",!0);function Vn(){return rn(this.year())}function ki(f,p,w,S,F,j,se){var Pe;return f<100&&f>=0?(Pe=new Date(f+400,p,w,S,F,j,se),isFinite(Pe.getFullYear())&&Pe.setFullYear(f)):Pe=new Date(f,p,w,S,F,j,se),Pe}function kr(f){var p,w;return f<100&&f>=0?(w=Array.prototype.slice.call(arguments),w[0]=f+400,p=new Date(Date.UTC.apply(null,w)),isFinite(p.getUTCFullYear())&&p.setUTCFullYear(f)):p=new Date(Date.UTC.apply(null,arguments)),p}function Jr(f,p,w){var S=7+p-w,F=(7+kr(f,0,S).getUTCDay()-p)%7;return-F+S-1}function My(f,p,w,S,F){var j=(7+w-S)%7,se=Jr(f,S,F),Pe=1+7*(p-1)+j+se,ut,$t;return Pe<=0?(ut=f-1,$t=qt(ut)+Pe):Pe>qt(f)?(ut=f+1,$t=Pe-qt(f)):(ut=f,$t=Pe),{year:ut,dayOfYear:$t}}function Ns(f,p,w){var S=Jr(f.year(),p,w),F=Math.floor((f.dayOfYear()-S-1)/7)+1,j,se;return F<1?(se=f.year()-1,j=F+xi(se,p,w)):F>xi(f.year(),p,w)?(j=F-xi(f.year(),p,w),se=f.year()+1):(se=f.year(),j=F),{week:j,year:se}}function xi(f,p,w){var S=Jr(f,p,w),F=Jr(f+1,p,w);return(qt(f)-S+F)/7}N(\"w\",[\"ww\",2],\"wo\",\"week\"),N(\"W\",[\"WW\",2],\"Wo\",\"isoWeek\"),Rt(\"week\",\"w\"),Rt(\"isoWeek\",\"W\"),Bt(\"week\",5),Bt(\"isoWeek\",5),ae(\"w\",L),ae(\"ww\",L,Ht),ae(\"W\",L),ae(\"WW\",L,Ht),Ft([\"w\",\"ww\",\"W\",\"WW\"],function(f,p,w,S){p[S.substr(0,1)]=Z(f)});function j_(f){return Ns(f,this._week.dow,this._week.doy).week}var q_={dow:0,doy:6};function G_(){return this._week.dow}function J_(){return this._week.doy}function Z_(f){var p=this.localeData().week(this);return f==null?p:this.add((f-p)*7,\"d\")}function Q_(f){var p=Ns(this,1,4).week;return f==null?p:this.add((f-p)*7,\"d\")}N(\"d\",0,\"do\",\"day\"),N(\"dd\",0,0,function(f){return this.localeData().weekdaysMin(this,f)}),N(\"ddd\",0,0,function(f){return this.localeData().weekdaysShort(this,f)}),N(\"dddd\",0,0,function(f){return this.localeData().weekdays(this,f)}),N(\"e\",0,0,\"weekday\"),N(\"E\",0,0,\"isoWeekday\"),Rt(\"day\",\"d\"),Rt(\"weekday\",\"e\"),Rt(\"isoWeekday\",\"E\"),Bt(\"day\",11),Bt(\"weekday\",11),Bt(\"isoWeekday\",11),ae(\"d\",L),ae(\"e\",L),ae(\"E\",L),ae(\"dd\",function(f,p){return p.weekdaysMinRegex(f)}),ae(\"ddd\",function(f,p){return p.weekdaysShortRegex(f)}),ae(\"dddd\",function(f,p){return p.weekdaysRegex(f)}),Ft([\"dd\",\"ddd\",\"dddd\"],function(f,p,w,S){var F=w._locale.weekdaysParse(f,S,w._strict);F!=null?p.d=F:g(w).invalidWeekday=f}),Ft([\"d\",\"e\",\"E\"],function(f,p,w,S){p[S]=Z(f)});function X_(f,p){return typeof f!=\"string\"?f:isNaN(f)?(f=p.weekdaysParse(f),typeof f==\"number\"?f:null):parseInt(f,10)}function e1(f,p){return typeof f==\"string\"?p.weekdaysParse(f)%7||7:isNaN(f)?null:f}function bf(f,p){return f.slice(p,7).concat(f.slice(0,p))}var t1=\"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday\".split(\"_\"),Ty=\"Sun_Mon_Tue_Wed_Thu_Fri_Sat\".split(\"_\"),n1=\"Su_Mo_Tu_We_Th_Fr_Sa\".split(\"_\"),r1=ye,i1=ye,a1=ye;function o1(f,p){var w=n(this._weekdays)?this._weekdays:this._weekdays[f&&f!==!0&&this._weekdays.isFormat.test(p)?\"format\":\"standalone\"];return f===!0?bf(w,this._week.dow):f?w[f.day()]:w}function s1(f){return f===!0?bf(this._weekdaysShort,this._week.dow):f?this._weekdaysShort[f.day()]:this._weekdaysShort}function l1(f){return f===!0?bf(this._weekdaysMin,this._week.dow):f?this._weekdaysMin[f.day()]:this._weekdaysMin}function u1(f,p,w){var S,F,j,se=f.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],S=0;S<7;++S)j=m([2e3,1]).day(S),this._minWeekdaysParse[S]=this.weekdaysMin(j,\"\").toLocaleLowerCase(),this._shortWeekdaysParse[S]=this.weekdaysShort(j,\"\").toLocaleLowerCase(),this._weekdaysParse[S]=this.weekdays(j,\"\").toLocaleLowerCase();return w?p===\"dddd\"?(F=_.call(this._weekdaysParse,se),F!==-1?F:null):p===\"ddd\"?(F=_.call(this._shortWeekdaysParse,se),F!==-1?F:null):(F=_.call(this._minWeekdaysParse,se),F!==-1?F:null):p===\"dddd\"?(F=_.call(this._weekdaysParse,se),F!==-1||(F=_.call(this._shortWeekdaysParse,se),F!==-1)?F:(F=_.call(this._minWeekdaysParse,se),F!==-1?F:null)):p===\"ddd\"?(F=_.call(this._shortWeekdaysParse,se),F!==-1||(F=_.call(this._weekdaysParse,se),F!==-1)?F:(F=_.call(this._minWeekdaysParse,se),F!==-1?F:null)):(F=_.call(this._minWeekdaysParse,se),F!==-1||(F=_.call(this._weekdaysParse,se),F!==-1)?F:(F=_.call(this._shortWeekdaysParse,se),F!==-1?F:null))}function c1(f,p,w){var S,F,j;if(this._weekdaysParseExact)return u1.call(this,f,p,w);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),S=0;S<7;S++){if(F=m([2e3,1]).day(S),w&&!this._fullWeekdaysParse[S]&&(this._fullWeekdaysParse[S]=new RegExp(\"^\"+this.weekdays(F,\"\").replace(\".\",\"\\\\.?\")+\"$\",\"i\"),this._shortWeekdaysParse[S]=new RegExp(\"^\"+this.weekdaysShort(F,\"\").replace(\".\",\"\\\\.?\")+\"$\",\"i\"),this._minWeekdaysParse[S]=new RegExp(\"^\"+this.weekdaysMin(F,\"\").replace(\".\",\"\\\\.?\")+\"$\",\"i\")),this._weekdaysParse[S]||(j=\"^\"+this.weekdays(F,\"\")+\"|^\"+this.weekdaysShort(F,\"\")+\"|^\"+this.weekdaysMin(F,\"\"),this._weekdaysParse[S]=new RegExp(j.replace(\".\",\"\"),\"i\")),w&&p===\"dddd\"&&this._fullWeekdaysParse[S].test(f))return S;if(w&&p===\"ddd\"&&this._shortWeekdaysParse[S].test(f))return S;if(w&&p===\"dd\"&&this._minWeekdaysParse[S].test(f))return S;if(!w&&this._weekdaysParse[S].test(f))return S}}function d1(f){if(!this.isValid())return f!=null?this:NaN;var p=this._isUTC?this._d.getUTCDay():this._d.getDay();return f!=null?(f=X_(f,this.localeData()),this.add(f-p,\"d\")):p}function f1(f){if(!this.isValid())return f!=null?this:NaN;var p=(this.day()+7-this.localeData()._week.dow)%7;return f==null?p:this.add(f-p,\"d\")}function h1(f){if(!this.isValid())return f!=null?this:NaN;if(f!=null){var p=e1(f,this.localeData());return this.day(this.day()%7?p:p-7)}else return this.day()||7}function m1(f){return this._weekdaysParseExact?(a(this,\"_weekdaysRegex\")||Df.call(this),f?this._weekdaysStrictRegex:this._weekdaysRegex):(a(this,\"_weekdaysRegex\")||(this._weekdaysRegex=r1),this._weekdaysStrictRegex&&f?this._weekdaysStrictRegex:this._weekdaysRegex)}function p1(f){return this._weekdaysParseExact?(a(this,\"_weekdaysRegex\")||Df.call(this),f?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(a(this,\"_weekdaysShortRegex\")||(this._weekdaysShortRegex=i1),this._weekdaysShortStrictRegex&&f?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function g1(f){return this._weekdaysParseExact?(a(this,\"_weekdaysRegex\")||Df.call(this),f?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(a(this,\"_weekdaysMinRegex\")||(this._weekdaysMinRegex=a1),this._weekdaysMinStrictRegex&&f?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function Df(){function f(lr,Fi){return Fi.length-lr.length}var p=[],w=[],S=[],F=[],j,se,Pe,ut,$t;for(j=0;j<7;j++)se=m([2e3,1]).day(j),Pe=Te(this.weekdaysMin(se,\"\")),ut=Te(this.weekdaysShort(se,\"\")),$t=Te(this.weekdays(se,\"\")),p.push(Pe),w.push(ut),S.push($t),F.push(Pe),F.push(ut),F.push($t);p.sort(f),w.sort(f),S.sort(f),F.sort(f),this._weekdaysRegex=new RegExp(\"^(\"+F.join(\"|\")+\")\",\"i\"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp(\"^(\"+S.join(\"|\")+\")\",\"i\"),this._weekdaysShortStrictRegex=new RegExp(\"^(\"+w.join(\"|\")+\")\",\"i\"),this._weekdaysMinStrictRegex=new RegExp(\"^(\"+p.join(\"|\")+\")\",\"i\")}function Sf(){return this.hours()%12||12}function y1(){return this.hours()||24}N(\"H\",[\"HH\",2],0,\"hour\"),N(\"h\",[\"hh\",2],0,Sf),N(\"k\",[\"kk\",2],0,y1),N(\"hmm\",0,0,function(){return\"\"+Sf.apply(this)+De(this.minutes(),2)}),N(\"hmmss\",0,0,function(){return\"\"+Sf.apply(this)+De(this.minutes(),2)+De(this.seconds(),2)}),N(\"Hmm\",0,0,function(){return\"\"+this.hours()+De(this.minutes(),2)}),N(\"Hmmss\",0,0,function(){return\"\"+this.hours()+De(this.minutes(),2)+De(this.seconds(),2)});function Fy(f,p){N(f,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),p)})}Fy(\"a\",!0),Fy(\"A\",!1),Rt(\"hour\",\"h\"),Bt(\"hour\",13);function Iy(f,p){return p._meridiemParse}ae(\"a\",Iy),ae(\"A\",Iy),ae(\"H\",L),ae(\"h\",L),ae(\"k\",L),ae(\"HH\",L,Ht),ae(\"hh\",L,Ht),ae(\"kk\",L,Ht),ae(\"hmm\",K),ae(\"hmmss\",ee),ae(\"Hmm\",K),ae(\"Hmmss\",ee),We([\"H\",\"HH\"],Yt),We([\"k\",\"kk\"],function(f,p,w){var S=Z(f);p[Yt]=S===24?0:S}),We([\"a\",\"A\"],function(f,p,w){w._isPm=w._locale.isPM(f),w._meridiem=f}),We([\"h\",\"hh\"],function(f,p,w){p[Yt]=Z(f),g(w).bigHour=!0}),We(\"hmm\",function(f,p,w){var S=f.length-2;p[Yt]=Z(f.substr(0,S)),p[Bn]=Z(f.substr(S)),g(w).bigHour=!0}),We(\"hmmss\",function(f,p,w){var S=f.length-4,F=f.length-2;p[Yt]=Z(f.substr(0,S)),p[Bn]=Z(f.substr(S,2)),p[Er]=Z(f.substr(F)),g(w).bigHour=!0}),We(\"Hmm\",function(f,p,w){var S=f.length-2;p[Yt]=Z(f.substr(0,S)),p[Bn]=Z(f.substr(S))}),We(\"Hmmss\",function(f,p,w){var S=f.length-4,F=f.length-2;p[Yt]=Z(f.substr(0,S)),p[Bn]=Z(f.substr(S,2)),p[Er]=Z(f.substr(F))});function v1(f){return(f+\"\").toLowerCase().charAt(0)===\"p\"}var w1=/[ap]\\.?m?\\.?/i,b1=me(\"Hours\",!0);function D1(f,p,w){return f>11?w?\"pm\":\"PM\":w?\"am\":\"AM\"}var Ay={calendar:be,longDateFormat:vt,invalidDate:Tt,ordinal:en,dayOfMonthOrdinalParse:gn,relativeTime:vn,months:T,monthsShort:V,week:q_,weekdays:t1,weekdaysMin:n1,weekdaysShort:Ty,meridiemParse:w1},tn={},Rs={},Hs;function S1(f,p){var w,S=Math.min(f.length,p.length);for(w=0;w0;){if(F=nu(j.slice(0,w).join(\"-\")),F)return F;if(S&&S.length>=w&&S1(j,S)>=w-1)break;w--}p++}return Hs}function k1(f){return f.match(\"^[^/\\\\\\\\]*$\")!=null}function nu(f){var p=null,w;if(tn[f]===void 0&&typeof ls!=\"undefined\"&&ls&&ls.exports&&k1(f))try{p=Hs._abbr,w=require,w(\"./locale/\"+f),na(p)}catch(S){tn[f]=null}return tn[f]}function na(f,p){var w;return f&&(s(p)?w=Ci(f):w=Ef(f,p),w?Hs=w:typeof console!=\"undefined\"&&console.warn&&console.warn(\"Locale \"+f+\" not found. Did you forget to load it?\")),Hs._abbr}function Ef(f,p){if(p!==null){var w,S=Ay;if(p.abbr=f,tn[f]!=null)J(\"defineLocaleOverride\",\"use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info.\"),S=tn[f]._config;else if(p.parentLocale!=null)if(tn[p.parentLocale]!=null)S=tn[p.parentLocale]._config;else if(w=nu(p.parentLocale),w!=null)S=w._config;else return Rs[p.parentLocale]||(Rs[p.parentLocale]=[]),Rs[p.parentLocale].push({name:f,config:p}),null;return tn[f]=new re(te(S,p)),Rs[f]&&Rs[f].forEach(function(F){Ef(F.name,F.config)}),na(f),tn[f]}else return delete tn[f],null}function x1(f,p){if(p!=null){var w,S,F=Ay;tn[f]!=null&&tn[f].parentLocale!=null?tn[f].set(te(tn[f]._config,p)):(S=nu(f),S!=null&&(F=S._config),p=te(F,p),S==null&&(p.abbr=f),w=new re(p),w.parentLocale=tn[f],tn[f]=w),na(f)}else tn[f]!=null&&(tn[f].parentLocale!=null?(tn[f]=tn[f].parentLocale,f===na()&&na(f)):tn[f]!=null&&delete tn[f]);return tn[f]}function Ci(f){var p;if(f&&f._locale&&f._locale._abbr&&(f=f._locale._abbr),!f)return Hs;if(!n(f)){if(p=nu(f),p)return p;f=[f]}return E1(f)}function C1(){return ne(tn)}function kf(f){var p,w=f._a;return w&&g(f).overflow===-2&&(p=w[or]<0||w[or]>11?or:w[sr]<1||w[sr]>b(w[Ot],w[or])?sr:w[Yt]<0||w[Yt]>24||w[Yt]===24&&(w[Bn]!==0||w[Er]!==0||w[Gr]!==0)?Yt:w[Bn]<0||w[Bn]>59?Bn:w[Er]<0||w[Er]>59?Er:w[Gr]<0||w[Gr]>999?Gr:-1,g(f)._overflowDayOfYear&&(psr)&&(p=sr),g(f)._overflowWeeks&&p===-1&&(p=tu),g(f)._overflowWeekday&&p===-1&&(p=z),g(f).overflow=p),f}var _1=/^\\s*((?:[+-]\\d{6}|\\d{4})-(?:\\d\\d-\\d\\d|W\\d\\d-\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?::\\d\\d(?::\\d\\d(?:[.,]\\d+)?)?)?)([+-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,M1=/^\\s*((?:[+-]\\d{6}|\\d{4})(?:\\d\\d\\d\\d|W\\d\\d\\d|W\\d\\d|\\d\\d\\d|\\d\\d|))(?:(T| )(\\d\\d(?:\\d\\d(?:\\d\\d(?:[.,]\\d+)?)?)?)([+-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,T1=/Z|[+-]\\d\\d(?::?\\d\\d)?/,ru=[[\"YYYYYY-MM-DD\",/[+-]\\d{6}-\\d\\d-\\d\\d/],[\"YYYY-MM-DD\",/\\d{4}-\\d\\d-\\d\\d/],[\"GGGG-[W]WW-E\",/\\d{4}-W\\d\\d-\\d/],[\"GGGG-[W]WW\",/\\d{4}-W\\d\\d/,!1],[\"YYYY-DDD\",/\\d{4}-\\d{3}/],[\"YYYY-MM\",/\\d{4}-\\d\\d/,!1],[\"YYYYYYMMDD\",/[+-]\\d{10}/],[\"YYYYMMDD\",/\\d{8}/],[\"GGGG[W]WWE\",/\\d{4}W\\d{3}/],[\"GGGG[W]WW\",/\\d{4}W\\d{2}/,!1],[\"YYYYDDD\",/\\d{7}/],[\"YYYYMM\",/\\d{6}/,!1],[\"YYYY\",/\\d{4}/,!1]],xf=[[\"HH:mm:ss.SSSS\",/\\d\\d:\\d\\d:\\d\\d\\.\\d+/],[\"HH:mm:ss,SSSS\",/\\d\\d:\\d\\d:\\d\\d,\\d+/],[\"HH:mm:ss\",/\\d\\d:\\d\\d:\\d\\d/],[\"HH:mm\",/\\d\\d:\\d\\d/],[\"HHmmss.SSSS\",/\\d\\d\\d\\d\\d\\d\\.\\d+/],[\"HHmmss,SSSS\",/\\d\\d\\d\\d\\d\\d,\\d+/],[\"HHmmss\",/\\d\\d\\d\\d\\d\\d/],[\"HHmm\",/\\d\\d\\d\\d/],[\"HH\",/\\d\\d/]],F1=/^\\/?Date\\((-?\\d+)/i,I1=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\\s)?(\\d{1,2})\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s(\\d{2,4})\\s(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\\d{4}))$/,A1={UT:0,GMT:0,EDT:-4*60,EST:-5*60,CDT:-5*60,CST:-6*60,MDT:-6*60,MST:-7*60,PDT:-7*60,PST:-8*60};function Ly(f){var p,w,S=f._i,F=_1.exec(S)||M1.exec(S),j,se,Pe,ut,$t=ru.length,lr=xf.length;if(F){for(g(f).iso=!0,p=0,w=$t;pqt(se)||f._dayOfYear===0)&&(g(f)._overflowDayOfYear=!0),w=kr(se,0,f._dayOfYear),f._a[or]=w.getUTCMonth(),f._a[sr]=w.getUTCDate()),p=0;p<3&&f._a[p]==null;++p)f._a[p]=S[p]=F[p];for(;p<7;p++)f._a[p]=S[p]=f._a[p]==null?p===2?1:0:f._a[p];f._a[Yt]===24&&f._a[Bn]===0&&f._a[Er]===0&&f._a[Gr]===0&&(f._nextDay=!0,f._a[Yt]=0),f._d=(f._useUTC?kr:ki).apply(null,S),j=f._useUTC?f._d.getUTCDay():f._d.getDay(),f._tzm!=null&&f._d.setUTCMinutes(f._d.getUTCMinutes()-f._tzm),f._nextDay&&(f._a[Yt]=24),f._w&&typeof f._w.d!=\"undefined\"&&f._w.d!==j&&(g(f).weekdayMismatch=!0)}}function V1(f){var p,w,S,F,j,se,Pe,ut,$t;p=f._w,p.GG!=null||p.W!=null||p.E!=null?(j=1,se=4,w=bo(p.GG,f._a[Ot],Ns(Zt(),1,4).year),S=bo(p.W,1),F=bo(p.E,1),(F<1||F>7)&&(ut=!0)):(j=f._locale._week.dow,se=f._locale._week.doy,$t=Ns(Zt(),j,se),w=bo(p.gg,f._a[Ot],$t.year),S=bo(p.w,$t.week),p.d!=null?(F=p.d,(F<0||F>6)&&(ut=!0)):p.e!=null?(F=p.e+j,(p.e<0||p.e>6)&&(ut=!0)):F=j),S<1||S>xi(w,j,se)?g(f)._overflowWeeks=!0:ut!=null?g(f)._overflowWeekday=!0:(Pe=My(w,S,F,j,se),f._a[Ot]=Pe.year,f._dayOfYear=Pe.dayOfYear)}t.ISO_8601=function(){},t.RFC_2822=function(){};function _f(f){if(f._f===t.ISO_8601){Ly(f);return}if(f._f===t.RFC_2822){Py(f);return}f._a=[],g(f).empty=!0;var p=\"\"+f._i,w,S,F,j,se,Pe=p.length,ut=0,$t,lr;for(F=jt(f._f,f._locale).match(Ce)||[],lr=F.length,w=0;w0&&g(f).unusedInput.push(se),p=p.slice(p.indexOf(S)+S.length),ut+=S.length),it[j]?(S?g(f).empty=!1:g(f).unusedTokens.push(j),Hn(j,S,f)):f._strict&&!S&&g(f).unusedTokens.push(j);g(f).charsLeftOver=Pe-ut,p.length>0&&g(f).unusedInput.push(p),f._a[Yt]<=12&&g(f).bigHour===!0&&f._a[Yt]>0&&(g(f).bigHour=void 0),g(f).parsedDateParts=f._a.slice(0),g(f).meridiem=f._meridiem,f._a[Yt]=$1(f._locale,f._a[Yt],f._meridiem),$t=g(f).era,$t!==null&&(f._a[Ot]=f._locale.erasConvertYear($t,f._a[Ot])),Cf(f),kf(f)}function $1(f,p,w){var S;return w==null?p:f.meridiemHour!=null?f.meridiemHour(p,w):(f.isPM!=null&&(S=f.isPM(w),S&&p<12&&(p+=12),!S&&p===12&&(p=0)),p)}function U1(f){var p,w,S,F,j,se,Pe=!1,ut=f._f.length;if(ut===0){g(f).invalidFormat=!0,f._d=new Date(NaN);return}for(F=0;Fthis?this:f:D()});function Hy(f,p){var w,S;if(p.length===1&&n(p[0])&&(p=p[0]),!p.length)return Zt();for(w=p[0],S=1;Sthis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function uM(){if(!s(this._isDSTShifted))return this._isDSTShifted;var f={},p;return x(f,this),f=Ny(f),f._a?(p=f._isUTC?m(f._a):Zt(f._a),this._isDSTShifted=this.isValid()&&eM(f._a,p.toArray())>0):this._isDSTShifted=!1,this._isDSTShifted}function cM(){return this.isValid()?!this._isUTC:!1}function dM(){return this.isValid()?this._isUTC:!1}function Vy(){return this.isValid()?this._isUTC&&this._offset===0:!1}var fM=/^(-|\\+)?(?:(\\d*)[. ])?(\\d+):(\\d+)(?::(\\d+)(\\.\\d*)?)?$/,hM=/^(-|\\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function Zr(f,p){var w=f,S=null,F,j,se;return au(f)?w={ms:f._milliseconds,d:f._days,M:f._months}:u(f)||!isNaN(+f)?(w={},p?w[p]=+f:w.milliseconds=+f):(S=fM.exec(f))?(F=S[1]===\"-\"?-1:1,w={y:0,d:Z(S[sr])*F,h:Z(S[Yt])*F,m:Z(S[Bn])*F,s:Z(S[Er])*F,ms:Z(Mf(S[Gr]*1e3))*F}):(S=hM.exec(f))?(F=S[1]===\"-\"?-1:1,w={y:Ha(S[2],F),M:Ha(S[3],F),w:Ha(S[4],F),d:Ha(S[5],F),h:Ha(S[6],F),m:Ha(S[7],F),s:Ha(S[8],F)}):w==null?w={}:typeof w==\"object\"&&(\"from\"in w||\"to\"in w)&&(se=mM(Zt(w.from),Zt(w.to)),w={},w.ms=se.milliseconds,w.M=se.months),j=new iu(w),au(f)&&a(f,\"_locale\")&&(j._locale=f._locale),au(f)&&a(f,\"_isValid\")&&(j._isValid=f._isValid),j}Zr.fn=iu.prototype,Zr.invalid=X1;function Ha(f,p){var w=f&&parseFloat(f.replace(\",\",\".\"));return(isNaN(w)?0:w)*p}function $y(f,p){var w={};return w.months=p.month()-f.month()+(p.year()-f.year())*12,f.clone().add(w.months,\"M\").isAfter(p)&&--w.months,w.milliseconds=+p-+f.clone().add(w.months,\"M\"),w}function mM(f,p){var w;return f.isValid()&&p.isValid()?(p=Ff(p,f),f.isBefore(p)?w=$y(f,p):(w=$y(p,f),w.milliseconds=-w.milliseconds,w.months=-w.months),w):{milliseconds:0,months:0}}function Uy(f,p){return function(w,S){var F,j;return S!==null&&!isNaN(+S)&&(J(p,\"moment().\"+p+\"(period, number) is deprecated. Please use moment().\"+p+\"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.\"),j=w,w=S,S=j),F=Zr(w,S),Wy(this,F,f),this}}function Wy(f,p,w,S){var F=p._milliseconds,j=Mf(p._days),se=Mf(p._months);f.isValid()&&(S=S==null?!0:S,se&&Qe(f,Ie(f,\"Month\")+se*w),j&&at(f,\"Date\",Ie(f,\"Date\")+j*w),F&&f._d.setTime(f._d.valueOf()+F*w),S&&t.updateOffset(f,j||se))}var pM=Uy(1,\"add\"),gM=Uy(-1,\"subtract\");function Yy(f){return typeof f==\"string\"||f instanceof String}function yM(f){return A(f)||l(f)||Yy(f)||u(f)||wM(f)||vM(f)||f===null||f===void 0}function vM(f){var p=i(f)&&!o(f),w=!1,S=[\"years\",\"year\",\"y\",\"months\",\"month\",\"M\",\"days\",\"day\",\"d\",\"dates\",\"date\",\"D\",\"hours\",\"hour\",\"h\",\"minutes\",\"minute\",\"m\",\"seconds\",\"second\",\"s\",\"milliseconds\",\"millisecond\",\"ms\"],F,j,se=S.length;for(F=0;Fw.valueOf():w.valueOf()9999?Mt(w,p?\"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]\":\"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ\"):Q(Date.prototype.toISOString)?p?this.toDate().toISOString():new Date(this.valueOf()+this.utcOffset()*60*1e3).toISOString().replace(\"Z\",Mt(w,\"Z\")):Mt(w,p?\"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]\":\"YYYY-MM-DD[T]HH:mm:ss.SSSZ\")}function OM(){if(!this.isValid())return\"moment.invalid(/* \"+this._i+\" */)\";var f=\"moment\",p=\"\",w,S,F,j;return this.isLocal()||(f=this.utcOffset()===0?\"moment.utc\":\"moment.parseZone\",p=\"Z\"),w=\"[\"+f+'(\"]',S=0<=this.year()&&this.year()<=9999?\"YYYY\":\"YYYYYY\",F=\"-MM-DD[T]HH:mm:ss.SSS\",j=p+'[\")]',this.format(w+S+F+j)}function LM(f){f||(f=this.isUtc()?t.defaultFormatUtc:t.defaultFormat);var p=Mt(this,f);return this.localeData().postformat(p)}function PM(f,p){return this.isValid()&&(A(f)&&f.isValid()||Zt(f).isValid())?Zr({to:this,from:f}).locale(this.locale()).humanize(!p):this.localeData().invalidDate()}function NM(f){return this.from(Zt(),f)}function RM(f,p){return this.isValid()&&(A(f)&&f.isValid()||Zt(f).isValid())?Zr({from:this,to:f}).locale(this.locale()).humanize(!p):this.localeData().invalidDate()}function HM(f){return this.to(Zt(),f)}function zy(f){var p;return f===void 0?this._locale._abbr:(p=Ci(f),p!=null&&(this._locale=p),this)}var Ky=B(\"moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.\",function(f){return f===void 0?this.localeData():this.locale(f)});function jy(){return this._locale}var su=1e3,Do=60*su,lu=60*Do,qy=(365*400+97)*24*lu;function So(f,p){return(f%p+p)%p}function Gy(f,p,w){return f<100&&f>=0?new Date(f+400,p,w)-qy:new Date(f,p,w).valueOf()}function Jy(f,p,w){return f<100&&f>=0?Date.UTC(f+400,p,w)-qy:Date.UTC(f,p,w)}function BM(f){var p,w;if(f=Vt(f),f===void 0||f===\"millisecond\"||!this.isValid())return this;switch(w=this._isUTC?Jy:Gy,f){case\"year\":p=w(this.year(),0,1);break;case\"quarter\":p=w(this.year(),this.month()-this.month()%3,1);break;case\"month\":p=w(this.year(),this.month(),1);break;case\"week\":p=w(this.year(),this.month(),this.date()-this.weekday());break;case\"isoWeek\":p=w(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case\"day\":case\"date\":p=w(this.year(),this.month(),this.date());break;case\"hour\":p=this._d.valueOf(),p-=So(p+(this._isUTC?0:this.utcOffset()*Do),lu);break;case\"minute\":p=this._d.valueOf(),p-=So(p,Do);break;case\"second\":p=this._d.valueOf(),p-=So(p,su);break}return this._d.setTime(p),t.updateOffset(this,!0),this}function VM(f){var p,w;if(f=Vt(f),f===void 0||f===\"millisecond\"||!this.isValid())return this;switch(w=this._isUTC?Jy:Gy,f){case\"year\":p=w(this.year()+1,0,1)-1;break;case\"quarter\":p=w(this.year(),this.month()-this.month()%3+3,1)-1;break;case\"month\":p=w(this.year(),this.month()+1,1)-1;break;case\"week\":p=w(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case\"isoWeek\":p=w(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case\"day\":case\"date\":p=w(this.year(),this.month(),this.date()+1)-1;break;case\"hour\":p=this._d.valueOf(),p+=lu-So(p+(this._isUTC?0:this.utcOffset()*Do),lu)-1;break;case\"minute\":p=this._d.valueOf(),p+=Do-So(p,Do)-1;break;case\"second\":p=this._d.valueOf(),p+=su-So(p,su)-1;break}return this._d.setTime(p),t.updateOffset(this,!0),this}function $M(){return this._d.valueOf()-(this._offset||0)*6e4}function UM(){return Math.floor(this.valueOf()/1e3)}function WM(){return new Date(this.valueOf())}function YM(){var f=this;return[f.year(),f.month(),f.date(),f.hour(),f.minute(),f.second(),f.millisecond()]}function zM(){var f=this;return{years:f.year(),months:f.month(),date:f.date(),hours:f.hours(),minutes:f.minutes(),seconds:f.seconds(),milliseconds:f.milliseconds()}}function KM(){return this.isValid()?this.toISOString():null}function jM(){return v(this)}function qM(){return d({},g(this))}function GM(){return g(this).overflow}function JM(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}}N(\"N\",0,0,\"eraAbbr\"),N(\"NN\",0,0,\"eraAbbr\"),N(\"NNN\",0,0,\"eraAbbr\"),N(\"NNNN\",0,0,\"eraName\"),N(\"NNNNN\",0,0,\"eraNarrow\"),N(\"y\",[\"y\",1],\"yo\",\"eraYear\"),N(\"y\",[\"yy\",2],0,\"eraYear\"),N(\"y\",[\"yyy\",3],0,\"eraYear\"),N(\"y\",[\"yyyy\",4],0,\"eraYear\"),ae(\"N\",Af),ae(\"NN\",Af),ae(\"NNN\",Af),ae(\"NNNN\",sT),ae(\"NNNNN\",lT),We([\"N\",\"NN\",\"NNN\",\"NNNN\",\"NNNNN\"],function(f,p,w,S){var F=w._locale.erasParse(f,S,w._strict);F?g(w).era=F:g(w).invalidEra=f}),ae(\"y\",ge),ae(\"yy\",ge),ae(\"yyy\",ge),ae(\"yyyy\",ge),ae(\"yo\",uT),We([\"y\",\"yy\",\"yyy\",\"yyyy\"],Ot),We([\"yo\"],function(f,p,w,S){var F;w._locale._eraYearOrdinalRegex&&(F=f.match(w._locale._eraYearOrdinalRegex)),w._locale.eraYearOrdinalParse?p[Ot]=w._locale.eraYearOrdinalParse(f,F):p[Ot]=parseInt(f,10)});function ZM(f,p){var w,S,F,j=this._eras||Ci(\"en\")._eras;for(w=0,S=j.length;w=0)return j[S]}function XM(f,p){var w=f.since<=f.until?1:-1;return p===void 0?t(f.since).year():t(f.since).year()+(p-f.offset)*w}function eT(){var f,p,w,S=this.localeData().eras();for(f=0,p=S.length;fj&&(p=j),gT.call(this,f,p,w,S,F))}function gT(f,p,w,S,F){var j=My(f,p,w,S,F),se=kr(j.year,0,j.dayOfYear);return this.year(se.getUTCFullYear()),this.month(se.getUTCMonth()),this.date(se.getUTCDate()),this}N(\"Q\",0,\"Qo\",\"quarter\"),Rt(\"quarter\",\"Q\"),Bt(\"quarter\",7),ae(\"Q\",_n),We(\"Q\",function(f,p){p[or]=(Z(f)-1)*3});function yT(f){return f==null?Math.ceil((this.month()+1)/3):this.month((f-1)*3+this.month()%3)}N(\"D\",[\"DD\",2],\"Do\",\"date\"),Rt(\"date\",\"D\"),Bt(\"date\",9),ae(\"D\",L),ae(\"DD\",L,Ht),ae(\"Do\",function(f,p){return f?p._dayOfMonthOrdinalParse||p._ordinalParse:p._dayOfMonthOrdinalParseLenient}),We([\"D\",\"DD\"],sr),We(\"Do\",function(f,p){p[sr]=Z(f.match(L)[0])});var Qy=me(\"Date\",!0);N(\"DDD\",[\"DDDD\",3],\"DDDo\",\"dayOfYear\"),Rt(\"dayOfYear\",\"DDD\"),Bt(\"dayOfYear\",4),ae(\"DDD\",ue),ae(\"DDDD\",ar),We([\"DDD\",\"DDDD\"],function(f,p,w){w._dayOfYear=Z(f)});function vT(f){var p=Math.round((this.clone().startOf(\"day\")-this.clone().startOf(\"year\"))/864e5)+1;return f==null?p:this.add(f-p,\"d\")}N(\"m\",[\"mm\",2],0,\"minute\"),Rt(\"minute\",\"m\"),Bt(\"minute\",14),ae(\"m\",L),ae(\"mm\",L,Ht),We([\"m\",\"mm\"],Bn);var wT=me(\"Minutes\",!1);N(\"s\",[\"ss\",2],0,\"second\"),Rt(\"second\",\"s\"),Bt(\"second\",15),ae(\"s\",L),ae(\"ss\",L,Ht),We([\"s\",\"ss\"],Er);var bT=me(\"Seconds\",!1);N(\"S\",0,0,function(){return~~(this.millisecond()/100)}),N(0,[\"SS\",2],0,function(){return~~(this.millisecond()/10)}),N(0,[\"SSS\",3],0,\"millisecond\"),N(0,[\"SSSS\",4],0,function(){return this.millisecond()*10}),N(0,[\"SSSSS\",5],0,function(){return this.millisecond()*100}),N(0,[\"SSSSSS\",6],0,function(){return this.millisecond()*1e3}),N(0,[\"SSSSSSS\",7],0,function(){return this.millisecond()*1e4}),N(0,[\"SSSSSSSS\",8],0,function(){return this.millisecond()*1e5}),N(0,[\"SSSSSSSSS\",9],0,function(){return this.millisecond()*1e6}),Rt(\"millisecond\",\"ms\"),Bt(\"millisecond\",16),ae(\"S\",ue,_n),ae(\"SS\",ue,Ht),ae(\"SSS\",ue,ar);var ra,Xy;for(ra=\"SSSS\";ra.length<=9;ra+=\"S\")ae(ra,ge);function DT(f,p){p[Gr]=Z((\"0.\"+f)*1e3)}for(ra=\"S\";ra.length<=9;ra+=\"S\")We(ra,DT);Xy=me(\"Milliseconds\",!1),N(\"z\",0,0,\"zoneAbbr\"),N(\"zz\",0,0,\"zoneName\");function ST(){return this._isUTC?\"UTC\":\"\"}function ET(){return this._isUTC?\"Coordinated Universal Time\":\"\"}var we=O.prototype;we.add=pM,we.calendar=SM,we.clone=EM,we.diff=FM,we.endOf=VM,we.format=LM,we.from=PM,we.fromNow=NM,we.to=RM,we.toNow=HM,we.get=Dt,we.invalidAt=GM,we.isAfter=kM,we.isBefore=xM,we.isBetween=CM,we.isSame=_M,we.isSameOrAfter=MM,we.isSameOrBefore=TM,we.isValid=jM,we.lang=Ky,we.locale=zy,we.localeData=jy,we.max=j1,we.min=K1,we.parsingFlags=qM,we.set=Cn,we.startOf=BM,we.subtract=gM,we.toArray=YM,we.toObject=zM,we.toDate=WM,we.toISOString=AM,we.inspect=OM,typeof Symbol!=\"undefined\"&&Symbol.for!=null&&(we[Symbol.for(\"nodejs.util.inspect.custom\")]=function(){return\"Moment<\"+this.format()+\">\"}),we.toJSON=KM,we.toString=IM,we.unix=UM,we.valueOf=$M,we.creationData=JM,we.eraName=eT,we.eraNarrow=tT,we.eraAbbr=nT,we.eraYear=rT,we.year=On,we.isLeapYear=Vn,we.weekYear=cT,we.isoWeekYear=dT,we.quarter=we.quarters=yT,we.month=ot,we.daysInMonth=qe,we.week=we.weeks=Z_,we.isoWeek=we.isoWeeks=Q_,we.weeksInYear=mT,we.weeksInWeekYear=pT,we.isoWeeksInYear=fT,we.isoWeeksInISOWeekYear=hT,we.date=Qy,we.day=we.days=d1,we.weekday=f1,we.isoWeekday=h1,we.dayOfYear=vT,we.hour=we.hours=b1,we.minute=we.minutes=wT,we.second=we.seconds=bT,we.millisecond=we.milliseconds=Xy,we.utcOffset=nM,we.utc=iM,we.local=aM,we.parseZone=oM,we.hasAlignedHourOffset=sM,we.isDST=lM,we.isLocal=cM,we.isUtcOffset=dM,we.isUtc=Vy,we.isUTC=Vy,we.zoneAbbr=ST,we.zoneName=ET,we.dates=B(\"dates accessor is deprecated. Use date instead.\",Qy),we.months=B(\"months accessor is deprecated. Use month instead\",ot),we.years=B(\"years accessor is deprecated. Use year instead\",On),we.zone=B(\"moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/\",rM),we.isDSTShifted=B(\"isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information\",uM);function kT(f){return Zt(f*1e3)}function xT(){return Zt.apply(null,arguments).parseZone()}function ev(f){return f}var Lt=re.prototype;Lt.calendar=pe,Lt.longDateFormat=Wt,Lt.invalidDate=Jt,Lt.ordinal=yn,Lt.preparse=ev,Lt.postformat=ev,Lt.relativeTime=xn,Lt.pastFuture=jn,Lt.set=oe,Lt.eras=ZM,Lt.erasParse=QM,Lt.erasConvertYear=XM,Lt.erasAbbrRegex=aT,Lt.erasNameRegex=iT,Lt.erasNarrowRegex=oT,Lt.months=he,Lt.monthsShort=Me,Lt.monthsParse=Le,Lt.monthsRegex=st,Lt.monthsShortRegex=yt,Lt.week=j_,Lt.firstDayOfYear=J_,Lt.firstDayOfWeek=G_,Lt.weekdays=o1,Lt.weekdaysMin=l1,Lt.weekdaysShort=s1,Lt.weekdaysParse=c1,Lt.weekdaysRegex=m1,Lt.weekdaysShortRegex=p1,Lt.weekdaysMinRegex=g1,Lt.isPM=v1,Lt.meridiem=D1;function cu(f,p,w,S){var F=Ci(),j=m().set(S,p);return F[w](j,f)}function tv(f,p,w){if(u(f)&&(p=f,f=void 0),f=f||\"\",p!=null)return cu(f,p,w,\"month\");var S,F=[];for(S=0;S<12;S++)F[S]=cu(f,S,w,\"month\");return F}function Lf(f,p,w,S){typeof f==\"boolean\"?(u(p)&&(w=p,p=void 0),p=p||\"\"):(p=f,w=p,f=!1,u(p)&&(w=p,p=void 0),p=p||\"\");var F=Ci(),j=f?F._week.dow:0,se,Pe=[];if(w!=null)return cu(p,(w+j)%7,S,\"day\");for(se=0;se<7;se++)Pe[se]=cu(p,(se+j)%7,S,\"day\");return Pe}function CT(f,p){return tv(f,p,\"months\")}function _T(f,p){return tv(f,p,\"monthsShort\")}function MT(f,p,w){return Lf(f,p,w,\"weekdays\")}function TT(f,p,w){return Lf(f,p,w,\"weekdaysShort\")}function FT(f,p,w){return Lf(f,p,w,\"weekdaysMin\")}na(\"en\",{eras:[{since:\"0001-01-01\",until:1/0,offset:1,name:\"Anno Domini\",narrow:\"AD\",abbr:\"AD\"},{since:\"0000-12-31\",until:-1/0,offset:1,name:\"Before Christ\",narrow:\"BC\",abbr:\"BC\"}],dayOfMonthOrdinalParse:/\\d{1,2}(th|st|nd|rd)/,ordinal:function(f){var p=f%10,w=Z(f%100/10)===1?\"th\":p===1?\"st\":p===2?\"nd\":p===3?\"rd\":\"th\";return f+w}}),t.lang=B(\"moment.lang is deprecated. Use moment.locale instead.\",na),t.langData=B(\"moment.langData is deprecated. Use moment.localeData instead.\",Ci);var _i=Math.abs;function IT(){var f=this._data;return this._milliseconds=_i(this._milliseconds),this._days=_i(this._days),this._months=_i(this._months),f.milliseconds=_i(f.milliseconds),f.seconds=_i(f.seconds),f.minutes=_i(f.minutes),f.hours=_i(f.hours),f.months=_i(f.months),f.years=_i(f.years),this}function nv(f,p,w,S){var F=Zr(p,w);return f._milliseconds+=S*F._milliseconds,f._days+=S*F._days,f._months+=S*F._months,f._bubble()}function AT(f,p){return nv(this,f,p,1)}function OT(f,p){return nv(this,f,p,-1)}function rv(f){return f<0?Math.floor(f):Math.ceil(f)}function LT(){var f=this._milliseconds,p=this._days,w=this._months,S=this._data,F,j,se,Pe,ut;return f>=0&&p>=0&&w>=0||f<=0&&p<=0&&w<=0||(f+=rv(Pf(w)+p)*864e5,p=0,w=0),S.milliseconds=f%1e3,F=At(f/1e3),S.seconds=F%60,j=At(F/60),S.minutes=j%60,se=At(j/60),S.hours=se%24,p+=At(se/24),ut=At(iv(p)),w+=ut,p-=rv(Pf(ut)),Pe=At(w/12),w%=12,S.days=p,S.months=w,S.years=Pe,this}function iv(f){return f*4800/146097}function Pf(f){return f*146097/4800}function PT(f){if(!this.isValid())return NaN;var p,w,S=this._milliseconds;if(f=Vt(f),f===\"month\"||f===\"quarter\"||f===\"year\")switch(p=this._days+S/864e5,w=this._months+iv(p),f){case\"month\":return w;case\"quarter\":return w/3;case\"year\":return w/12}else switch(p=this._days+Math.round(Pf(this._months)),f){case\"week\":return p/7+S/6048e5;case\"day\":return p+S/864e5;case\"hour\":return p*24+S/36e5;case\"minute\":return p*1440+S/6e4;case\"second\":return p*86400+S/1e3;case\"millisecond\":return Math.floor(p*864e5)+S;default:throw new Error(\"Unknown unit \"+f)}}function NT(){return this.isValid()?this._milliseconds+this._days*864e5+this._months%12*2592e6+Z(this._months/12)*31536e6:NaN}function Mi(f){return function(){return this.as(f)}}var RT=Mi(\"ms\"),HT=Mi(\"s\"),BT=Mi(\"m\"),VT=Mi(\"h\"),$T=Mi(\"d\"),UT=Mi(\"w\"),WT=Mi(\"M\"),YT=Mi(\"Q\"),zT=Mi(\"y\");function KT(){return Zr(this)}function jT(f){return f=Vt(f),this.isValid()?this[f+\"s\"]():NaN}function Ba(f){return function(){return this.isValid()?this._data[f]:NaN}}var qT=Ba(\"milliseconds\"),GT=Ba(\"seconds\"),JT=Ba(\"minutes\"),ZT=Ba(\"hours\"),QT=Ba(\"days\"),XT=Ba(\"months\"),eF=Ba(\"years\");function tF(){return At(this.days()/7)}var Ti=Math.round,Eo={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function nF(f,p,w,S,F){return F.relativeTime(p||1,!!w,f,S)}function rF(f,p,w,S){var F=Zr(f).abs(),j=Ti(F.as(\"s\")),se=Ti(F.as(\"m\")),Pe=Ti(F.as(\"h\")),ut=Ti(F.as(\"d\")),$t=Ti(F.as(\"M\")),lr=Ti(F.as(\"w\")),Fi=Ti(F.as(\"y\")),ia=j<=w.ss&&[\"s\",j]||j0,ia[4]=S,nF.apply(null,ia)}function iF(f){return f===void 0?Ti:typeof f==\"function\"?(Ti=f,!0):!1}function aF(f,p){return Eo[f]===void 0?!1:p===void 0?Eo[f]:(Eo[f]=p,f===\"s\"&&(Eo.ss=p-1),!0)}function oF(f,p){if(!this.isValid())return this.localeData().invalidDate();var w=!1,S=Eo,F,j;return typeof f==\"object\"&&(p=f,f=!1),typeof f==\"boolean\"&&(w=f),typeof p==\"object\"&&(S=Object.assign({},Eo,p),p.s!=null&&p.ss==null&&(S.ss=p.s-1)),F=this.localeData(),j=rF(this,!w,S,F),w&&(j=F.pastFuture(+this,j)),F.postformat(j)}var Nf=Math.abs;function ko(f){return(f>0)-(f<0)||+f}function du(){if(!this.isValid())return this.localeData().invalidDate();var f=Nf(this._milliseconds)/1e3,p=Nf(this._days),w=Nf(this._months),S,F,j,se,Pe=this.asSeconds(),ut,$t,lr,Fi;return Pe?(S=At(f/60),F=At(S/60),f%=60,S%=60,j=At(w/12),w%=12,se=f?f.toFixed(3).replace(/\\.?0+$/,\"\"):\"\",ut=Pe<0?\"-\":\"\",$t=ko(this._months)!==ko(Pe)?\"-\":\"\",lr=ko(this._days)!==ko(Pe)?\"-\":\"\",Fi=ko(this._milliseconds)!==ko(Pe)?\"-\":\"\",ut+\"P\"+(j?$t+j+\"Y\":\"\")+(w?$t+w+\"M\":\"\")+(p?lr+p+\"D\":\"\")+(F||S||f?\"T\":\"\")+(F?Fi+F+\"H\":\"\")+(S?Fi+S+\"M\":\"\")+(f?Fi+se+\"S\":\"\")):\"P0D\"}var xt=iu.prototype;xt.isValid=Q1,xt.abs=IT,xt.add=AT,xt.subtract=OT,xt.as=PT,xt.asMilliseconds=RT,xt.asSeconds=HT,xt.asMinutes=BT,xt.asHours=VT,xt.asDays=$T,xt.asWeeks=UT,xt.asMonths=WT,xt.asQuarters=YT,xt.asYears=zT,xt.valueOf=NT,xt._bubble=LT,xt.clone=KT,xt.get=jT,xt.milliseconds=qT,xt.seconds=GT,xt.minutes=JT,xt.hours=ZT,xt.days=QT,xt.weeks=tF,xt.months=XT,xt.years=eF,xt.humanize=oF,xt.toISOString=du,xt.toString=du,xt.toJSON=du,xt.locale=zy,xt.localeData=jy,xt.toIsoString=B(\"toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)\",du),xt.lang=Ky,N(\"X\",0,0,\"unix\"),N(\"x\",0,0,\"valueOf\"),ae(\"x\",M),ae(\"X\",ie),We(\"X\",function(f,p,w){w._d=new Date(parseFloat(f)*1e3)}),We(\"x\",function(f,p,w){w._d=new Date(Z(f))});return t.version=\"2.29.4\",r(Zt),t.fn=we,t.min=q1,t.max=G1,t.now=J1,t.utc=m,t.unix=kT,t.months=CT,t.isDate=l,t.locale=na,t.invalid=D,t.duration=Zr,t.isMoment=A,t.weekdays=MT,t.parseZone=xT,t.localeData=Ci,t.isDuration=au,t.monthsShort=_T,t.weekdaysMin=FT,t.defineLocale=Ef,t.updateLocale=x1,t.locales=C1,t.weekdaysShort=TT,t.normalizeUnits=Vt,t.relativeTimeRounding=iF,t.relativeTimeThreshold=aF,t.calendarFormat=DM,t.prototype=we,t.HTML5_FMT={DATETIME_LOCAL:\"YYYY-MM-DDTHH:mm\",DATETIME_LOCAL_SECONDS:\"YYYY-MM-DDTHH:mm:ss\",DATETIME_LOCAL_MS:\"YYYY-MM-DDTHH:mm:ss.SSS\",DATE:\"YYYY-MM-DD\",TIME:\"HH:mm\",TIME_SECONDS:\"HH:mm:ss\",TIME_MS:\"HH:mm:ss.SSS\",WEEK:\"GGGG-[W]WW\",MONTH:\"YYYY-MM\"},t})});var dx=wn((Fd,_g)=>{(function(t,r){typeof Fd==\"object\"&&typeof _g==\"object\"?_g.exports=r():typeof define==\"function\"&&define.amd?define([],r):typeof Fd==\"object\"?Fd.Choices=r():t.Choices=r()})(window,function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return r.m=e,r.c=t,r.d=function(n,i,a){r.o(n,i)||Object.defineProperty(n,i,{enumerable:!0,get:a})},r.r=function(n){typeof Symbol!=\"undefined\"&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(n,\"__esModule\",{value:!0})},r.t=function(n,i){if(i&1&&(n=r(n)),i&8||i&4&&typeof n==\"object\"&&n&&n.__esModule)return n;var a=Object.create(null);if(r.r(a),Object.defineProperty(a,\"default\",{enumerable:!0,value:n}),i&2&&typeof n!=\"string\")for(var o in n)r.d(a,o,function(s){return n[s]}.bind(null,o));return a},r.n=function(n){var i=n&&n.__esModule?function(){return n.default}:function(){return n};return r.d(i,\"a\",i),i},r.o=function(n,i){return Object.prototype.hasOwnProperty.call(n,i)},r.p=\"/public/assets/scripts/\",r(r.s=4)}([function(e,t,r){\"use strict\";var n=function(x){return i(x)&&!a(x)};function i(C){return!!C&&typeof C==\"object\"}function a(C){var x=Object.prototype.toString.call(C);return x===\"[object RegExp]\"||x===\"[object Date]\"||u(C)}var o=typeof Symbol==\"function\"&&Symbol.for,s=o?Symbol.for(\"react.element\"):60103;function u(C){return C.$$typeof===s}function l(C){return Array.isArray(C)?[]:{}}function c(C,x){return x.clone!==!1&&x.isMergeableObject(C)?D(l(C),C,x):C}function d(C,x,O){return C.concat(x).map(function(A){return c(A,O)})}function m(C,x){if(!x.customMerge)return D;var O=x.customMerge(C);return typeof O==\"function\"?O:D}function h(C){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(C).filter(function(x){return C.propertyIsEnumerable(x)}):[]}function g(C){return Object.keys(C).concat(h(C))}function y(C,x){try{return x in C&&!(Object.hasOwnProperty.call(C,x)&&Object.propertyIsEnumerable.call(C,x))}catch(O){return!1}}function v(C,x,O){var A={};return O.isMergeableObject(C)&&g(C).forEach(function(P){A[P]=c(C[P],O)}),g(x).forEach(function(P){y(C,P)||(!O.isMergeableObject(x[P])||!C[P]?A[P]=c(x[P],O):A[P]=m(P,O)(C[P],x[P],O))}),A}function D(C,x,O){O=O||{},O.arrayMerge=O.arrayMerge||d,O.isMergeableObject=O.isMergeableObject||n,O.cloneUnlessOtherwiseSpecified=c;var A=Array.isArray(x),P=Array.isArray(C),B=A===P;return B?A?O.arrayMerge(C,x,O):v(C,x,O):c(x,O)}D.all=function(x,O){if(!Array.isArray(x))throw new Error(\"first argument should be an array\");return x.reduce(function(A,P){return D(A,P,O)},{})};var I=D;e.exports=I},function(e,t,r){\"use strict\";(function(n,i){var a=r(3),o;typeof self!=\"undefined\"?o=self:typeof window!=\"undefined\"?o=window:typeof n!=\"undefined\"?o=n:o=i;var s=Object(a.a)(o);t.a=s}).call(this,r(5),r(6)(e))},function(e,t,r){(function(n,i){e.exports=i()})(this,function(){return function(n){var i={};function a(o){if(i[o])return i[o].exports;var s=i[o]={i:o,l:!1,exports:{}};return n[o].call(s.exports,s,s.exports,a),s.l=!0,s.exports}return a.m=n,a.c=i,a.d=function(o,s,u){a.o(o,s)||Object.defineProperty(o,s,{enumerable:!0,get:u})},a.r=function(o){typeof Symbol!=\"undefined\"&&Symbol.toStringTag&&Object.defineProperty(o,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(o,\"__esModule\",{value:!0})},a.t=function(o,s){if(1&s&&(o=a(o)),8&s||4&s&&typeof o==\"object\"&&o&&o.__esModule)return o;var u=Object.create(null);if(a.r(u),Object.defineProperty(u,\"default\",{enumerable:!0,value:o}),2&s&&typeof o!=\"string\")for(var l in o)a.d(u,l,function(c){return o[c]}.bind(null,l));return u},a.n=function(o){var s=o&&o.__esModule?function(){return o.default}:function(){return o};return a.d(s,\"a\",s),s},a.o=function(o,s){return Object.prototype.hasOwnProperty.call(o,s)},a.p=\"\",a(a.s=1)}([function(n,i){n.exports=function(a){return Array.isArray?Array.isArray(a):Object.prototype.toString.call(a)===\"[object Array]\"}},function(n,i,a){function o(m){return(o=typeof Symbol==\"function\"&&typeof Symbol.iterator==\"symbol\"?function(h){return typeof h}:function(h){return h&&typeof Symbol==\"function\"&&h.constructor===Symbol&&h!==Symbol.prototype?\"symbol\":typeof h})(m)}function s(m,h){for(var g=0;g1&&arguments[1]!==void 0?arguments[1]:{limit:!1};this._log(`---------\nSearch pattern: \"`.concat(v,'\"'));var I=this._prepareSearchers(v),C=I.tokenSearchers,x=I.fullSearcher,O=this._search(C,x),A=O.weights,P=O.results;return this._computeScore(A,P),this.options.shouldSort&&this._sort(P),D.limit&&typeof D.limit==\"number\"&&(P=P.slice(0,D.limit)),this._format(P)}},{key:\"_prepareSearchers\",value:function(){var v=arguments.length>0&&arguments[0]!==void 0?arguments[0]:\"\",D=[];if(this.options.tokenize)for(var I=v.split(this.options.tokenSeparator),C=0,x=I.length;C0&&arguments[0]!==void 0?arguments[0]:[],D=arguments.length>1?arguments[1]:void 0,I=this.list,C={},x=[];if(typeof I[0]==\"string\"){for(var O=0,A=I.length;O1)throw new Error(\"Key weight has to be > 0 and <= 1\");te=te.name}else P[te]={weight:1};this._analyze({key:te,value:this.options.getFn(J,te),record:J,index:B},{resultMap:C,results:x,tokenSearchers:v,fullSearcher:D})}return{weights:P,results:x}}},{key:\"_analyze\",value:function(v,D){var I=v.key,C=v.arrayIndex,x=C===void 0?-1:C,O=v.value,A=v.record,P=v.index,B=D.tokenSearchers,G=B===void 0?[]:B,J=D.fullSearcher,Q=J===void 0?[]:J,oe=D.resultMap,te=oe===void 0?{}:oe,re=D.results,ne=re===void 0?[]:re;if(O!=null){var be=!1,pe=-1,De=0;if(typeof O==\"string\"){this._log(`\nKey: `.concat(I===\"\"?\"-\":I));var Ce=Q.search(O);if(this._log('Full text: \"'.concat(O,'\", score: ').concat(Ce.score)),this.options.tokenize){for(var U=O.split(this.options.tokenSeparator),Je=[],it=0;it-1&&(Jt=(Jt+pe)/2),this._log(\"Score average:\",Jt);var en=!this.options.tokenize||!this.options.matchAllTokens||De>=G.length;if(this._log(`\nCheck Matches: `.concat(en)),(be||Ce.isMatch)&&en){var gn=te[P];gn?gn.output.push({key:I,arrayIndex:x,value:O,score:Jt,matchedIndices:Ce.matchedIndices}):(te[P]={item:A,output:[{key:I,arrayIndex:x,value:O,score:Jt,matchedIndices:Ce.matchedIndices}]},ne.push(te[P]))}}else if(c(O))for(var yn=0,vn=O.length;yn-1&&(be.arrayIndex=ne.arrayIndex),Q.matches.push(be)}}}),this.options.includeScore&&C.push(function(J,Q){Q.score=J.score});for(var x=0,O=v.length;xD)return s(y,this.pattern,I);var C=this.options,x=C.location,O=C.distance,A=C.threshold,P=C.findAllMatches,B=C.minMatchCharLength;return u(y,this.pattern,this.patternAlphabet,{location:x,distance:O,threshold:A,findAllMatches:P,minMatchCharLength:B})}}])&&o(m.prototype,h),g&&o(m,g),d}();n.exports=c},function(n,i){var a=/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g;n.exports=function(o,s){var u=arguments.length>2&&arguments[2]!==void 0?arguments[2]:/ +/g,l=new RegExp(s.replace(a,\"\\\\$&\").replace(u,\"|\")),c=o.match(l),d=!!c,m=[];if(d)for(var h=0,g=c.length;h=it;It-=1){var Mt=It-1,jt=c[u.charAt(Mt)];if(jt&&(Q[Mt]=1),Ze[It]=(Ze[It+1]<<1|1)&jt,Ce!==0&&(Ze[It]|=(ne[It+1]|ne[It])<<1|1|ne[It+1]),Ze[It]&De&&(be=o(l,{errors:Ce,currentLocation:Mt,expectedLocation:A,distance:y}))<=B){if(B=be,(G=Mt)<=A)break;it=Math.max(1,2*A-G)}}if(o(l,{errors:Ce+1,currentLocation:A,expectedLocation:A,distance:y})>B)break;ne=Ze}return{isMatch:G>=0,score:be===0?.001:be,matchedIndices:s(Q,O)}}},function(n,i){n.exports=function(a,o){var s=o.errors,u=s===void 0?0:s,l=o.currentLocation,c=l===void 0?0:l,d=o.expectedLocation,m=d===void 0?0:d,h=o.distance,g=h===void 0?100:h,y=u/a.length,v=Math.abs(m-c);return g?y+v/g:v?1:y}},function(n,i){n.exports=function(){for(var a=arguments.length>0&&arguments[0]!==void 0?arguments[0]:[],o=arguments.length>1&&arguments[1]!==void 0?arguments[1]:1,s=[],u=-1,l=-1,c=0,d=a.length;c=o&&s.push([u,l]),u=-1)}return a[c-1]&&c-u>=o&&s.push([u,c-1]),s}},function(n,i){n.exports=function(a){for(var o={},s=a.length,u=0;u0)return\"Unexpected \"+(Y.length>1?\"keys\":\"key\")+\" \"+('\"'+Y.join('\", \"')+'\" found in '+V+\". \")+\"Expected to find one of the known reducer keys instead: \"+('\"'+T.join('\", \"')+'\". Unexpected keys will be ignored.')}function y(z){Object.keys(z).forEach(function(k){var _=z[k],b=_(void 0,{type:l.INIT});if(typeof b==\"undefined\")throw new Error('Reducer \"'+k+`\" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined. If you don't want to set a value for this reducer, you can use null instead of undefined.`);if(typeof _(void 0,{type:l.PROBE_UNKNOWN_ACTION()})==\"undefined\")throw new Error('Reducer \"'+k+'\" returned undefined when probed with a random type. '+(\"Don't try to handle \"+l.INIT+' or other actions in \"redux/*\" ')+\"namespace. They are considered private. Instead, you must return the current state for any unknown actions, unless it is undefined, in which case you must return the initial state, regardless of the action type. The initial state may not be undefined, but can be null.\")})}function v(z){for(var k=Object.keys(z),_={},b=0;b-1?z.map(function(_){var b=_;return b.id===parseInt(k.choiceId,10)&&(b.selected=!0),b}):z;case\"REMOVE_ITEM\":return k.choiceId>-1?z.map(function(_){var b=_;return b.id===parseInt(k.choiceId,10)&&(b.selected=!1),b}):z;case\"FILTER_CHOICES\":return z.map(function(_){var b=_;return b.active=k.results.some(function(T){var V=T.item,Y=T.score;return V.id===b.id?(b.score=Y,!0):!1}),b});case\"ACTIVATE_CHOICES\":return z.map(function(_){var b=_;return b.active=k.active,b});case\"CLEAR_CHOICES\":return te;default:return z}}var ne={loading:!1},be=function(k,_){switch(k===void 0&&(k=ne),_.type){case\"SET_IS_LOADING\":return{loading:_.isLoading};default:return k}},pe=be,De=function(k,_){return Math.floor(Math.random()*(_-k)+k)},Ce=function(k){return Array.from({length:k},function(){return De(0,36).toString(36)}).join(\"\")},U=function(k,_){var b=k.id||k.name&&k.name+\"-\"+Ce(2)||Ce(4);return b=b.replace(/(:|\\.|\\[|\\]|,)/g,\"\"),b=_+\"-\"+b,b},Je=function(k){return Object.prototype.toString.call(k).slice(8,-1)},it=function(k,_){return _!=null&&Je(_)===k},N=function(k,_){return _===void 0&&(_=document.createElement(\"div\")),k.nextSibling?k.parentNode.insertBefore(_,k.nextSibling):k.parentNode.appendChild(_),_.appendChild(k)},Ze=function(k,_,b){if(b===void 0&&(b=1),!(!(k instanceof Element)||typeof _!=\"string\")){for(var T=(b>0?\"next\":\"previous\")+\"ElementSibling\",V=k[T];V;){if(V.matches(_))return V;V=V[T]}return V}},It=function(k,_,b){if(b===void 0&&(b=1),!k)return!1;var T;return b>0?T=_.scrollTop+_.offsetHeight>=k.offsetTop+k.offsetHeight:T=k.offsetTop>=_.scrollTop,T},Mt=function(k){return typeof k!=\"string\"?k:k.replace(/&/g,\"&\").replace(/>/g,\"&rt;\").replace(/\"'+Mt(k)+'\"'},maxItemText:function(k){return\"Only \"+k+\" values can be added\"},valueComparer:function(k,_){return k===_},fuseOptions:{includeScore:!0},callbackOnInit:null,callbackOnCreateTemplates:null,classNames:Bt},rn={showDropdown:\"showDropdown\",hideDropdown:\"hideDropdown\",change:\"change\",choice:\"choice\",search:\"search\",addItem:\"addItem\",removeItem:\"removeItem\",highlightItem:\"highlightItem\",highlightChoice:\"highlightChoice\"},At={ADD_CHOICE:\"ADD_CHOICE\",FILTER_CHOICES:\"FILTER_CHOICES\",ACTIVATE_CHOICES:\"ACTIVATE_CHOICES\",CLEAR_CHOICES:\"CLEAR_CHOICES\",ADD_GROUP:\"ADD_GROUP\",ADD_ITEM:\"ADD_ITEM\",REMOVE_ITEM:\"REMOVE_ITEM\",HIGHLIGHT_ITEM:\"HIGHLIGHT_ITEM\",CLEAR_ALL:\"CLEAR_ALL\"},Z={BACK_KEY:46,DELETE_KEY:8,ENTER_KEY:13,A_KEY:65,ESC_KEY:27,UP_KEY:38,DOWN_KEY:40,PAGE_UP_KEY:33,PAGE_DOWN_KEY:34},me=\"text\",Ie=\"select-one\",at=\"select-multiple\",Dt=4,Cn=function(){function z(_){var b=_.element,T=_.type,V=_.classNames,Y=_.position;this.element=b,this.classNames=V,this.type=T,this.position=Y,this.isOpen=!1,this.isFlipped=!1,this.isFocussed=!1,this.isDisabled=!1,this.isLoading=!1,this._onFocus=this._onFocus.bind(this),this._onBlur=this._onBlur.bind(this)}var k=z.prototype;return k.addEventListeners=function(){this.element.addEventListener(\"focus\",this._onFocus),this.element.addEventListener(\"blur\",this._onBlur)},k.removeEventListeners=function(){this.element.removeEventListener(\"focus\",this._onFocus),this.element.removeEventListener(\"blur\",this._onBlur)},k.shouldFlip=function(b){if(typeof b!=\"number\")return!1;var T=!1;return this.position===\"auto\"?T=!window.matchMedia(\"(min-height: \"+(b+1)+\"px)\").matches:this.position===\"top\"&&(T=!0),T},k.setActiveDescendant=function(b){this.element.setAttribute(\"aria-activedescendant\",b)},k.removeActiveDescendant=function(){this.element.removeAttribute(\"aria-activedescendant\")},k.open=function(b){this.element.classList.add(this.classNames.openState),this.element.setAttribute(\"aria-expanded\",\"true\"),this.isOpen=!0,this.shouldFlip(b)&&(this.element.classList.add(this.classNames.flippedState),this.isFlipped=!0)},k.close=function(){this.element.classList.remove(this.classNames.openState),this.element.setAttribute(\"aria-expanded\",\"false\"),this.removeActiveDescendant(),this.isOpen=!1,this.isFlipped&&(this.element.classList.remove(this.classNames.flippedState),this.isFlipped=!1)},k.focus=function(){this.isFocussed||this.element.focus()},k.addFocusState=function(){this.element.classList.add(this.classNames.focusState)},k.removeFocusState=function(){this.element.classList.remove(this.classNames.focusState)},k.enable=function(){this.element.classList.remove(this.classNames.disabledState),this.element.removeAttribute(\"aria-disabled\"),this.type===Ie&&this.element.setAttribute(\"tabindex\",\"0\"),this.isDisabled=!1},k.disable=function(){this.element.classList.add(this.classNames.disabledState),this.element.setAttribute(\"aria-disabled\",\"true\"),this.type===Ie&&this.element.setAttribute(\"tabindex\",\"-1\"),this.isDisabled=!0},k.wrap=function(b){N(b,this.element)},k.unwrap=function(b){this.element.parentNode.insertBefore(b,this.element),this.element.parentNode.removeChild(this.element)},k.addLoadingState=function(){this.element.classList.add(this.classNames.loadingState),this.element.setAttribute(\"aria-busy\",\"true\"),this.isLoading=!0},k.removeLoadingState=function(){this.element.classList.remove(this.classNames.loadingState),this.element.removeAttribute(\"aria-busy\"),this.isLoading=!1},k._onFocus=function(){this.isFocussed=!0},k._onBlur=function(){this.isFocussed=!1},z}();function _n(z,k){for(var _=0;_0?this.element.scrollTop+he-X:b.offsetTop;activeWindow.requestAnimationFrame(function(){V._animateScroll(Me,T)})}},k._scrollDown=function(b,T,V){var Y=(V-b)/T,X=Y>1?Y:1;this.element.scrollTop=b+X},k._scrollUp=function(b,T,V){var Y=(b-V)/T,X=Y>1?Y:1;this.element.scrollTop=b-X},k._animateScroll=function(b,T){var V=this,Y=Dt,X=this.element.scrollTop,le=!1;T>0?(this._scrollDown(X,Y,b),Xb&&(le=!0)),le&&activeWindow.requestAnimationFrame(function(){V._animateScroll(b,T)})},z}();function St(z,k){for(var _=0;_0?\"treeitem\":\"option\"),Object.assign(lt.dataset,{choice:\"\",id:Me,value:Ve,selectText:b}),qe?(lt.classList.add(le),lt.dataset.choiceDisabled=\"\",lt.setAttribute(\"aria-disabled\",\"true\")):(lt.classList.add(Y),lt.dataset.choiceSelectable=\"\"),lt},input:function(k,_){var b=k.input,T=k.inputCloned,V=Object.assign(document.createElement(\"input\"),{type:\"text\",className:b+\" \"+T,autocomplete:\"off\",autocapitalize:\"off\",spellcheck:!1});return V.setAttribute(\"role\",\"textbox\"),V.setAttribute(\"aria-autocomplete\",\"list\"),V.setAttribute(\"aria-label\",_),V},dropdown:function(k){var _=k.list,b=k.listDropdown,T=document.createElement(\"div\");return T.classList.add(_,b),T.setAttribute(\"aria-expanded\",\"false\"),T},notice:function(k,_,b){var T=k.item,V=k.itemChoice,Y=k.noResults,X=k.noChoices;b===void 0&&(b=\"\");var le=[T,V];return b===\"no-choices\"?le.push(X):b===\"no-results\"&&le.push(Y),Object.assign(document.createElement(\"div\"),{innerHTML:_,className:le.join(\" \")})},option:function(k){var _=k.label,b=k.value,T=k.customProperties,V=k.active,Y=k.disabled,X=new Option(_,b,!1,V);return T&&(X.dataset.customProperties=T),X.disabled=Y,X}},ye=ie,ce=function(k){var _=k.value,b=k.label,T=k.id,V=k.groupId,Y=k.disabled,X=k.elementId,le=k.customProperties,he=k.placeholder,Me=k.keyCode;return{type:At.ADD_CHOICE,value:_,label:b,id:T,groupId:V,disabled:Y,elementId:X,customProperties:le,placeholder:he,keyCode:Me}},ae=function(k){return{type:At.FILTER_CHOICES,results:k}},Se=function(k){return k===void 0&&(k=!0),{type:At.ACTIVATE_CHOICES,active:k}},nt=function(){return{type:At.CLEAR_CHOICES}},Te=function(k){var _=k.value,b=k.label,T=k.id,V=k.choiceId,Y=k.groupId,X=k.customProperties,le=k.placeholder,he=k.keyCode;return{type:At.ADD_ITEM,value:_,label:b,id:T,choiceId:V,groupId:Y,customProperties:X,placeholder:le,keyCode:he}},Ue=function(k,_){return{type:At.REMOVE_ITEM,id:k,choiceId:_}},We=function(k,_){return{type:At.HIGHLIGHT_ITEM,id:k,highlighted:_}},Ft=function(k){var _=k.value,b=k.id,T=k.active,V=k.disabled;return{type:At.ADD_GROUP,value:_,id:b,active:T,disabled:V}},Hn=function(){return{type:\"CLEAR_ALL\"}},Ot=function(k){return{type:\"RESET_TO\",state:k}},or=function(k){return{type:\"SET_IS_LOADING\",isLoading:k}};function sr(z,k){for(var _=0;_=0?this._store.getGroupById(X):null;return this._store.dispatch(We(V,!0)),T&&this.passedElement.triggerEvent(rn.highlightItem,{id:V,value:he,label:Ve,groupValue:Le&&Le.value?Le.value:null}),this},k.unhighlightItem=function(b){if(!b)return this;var T=b.id,V=b.groupId,Y=V===void 0?-1:V,X=b.value,le=X===void 0?\"\":X,he=b.label,Me=he===void 0?\"\":he,Ve=Y>=0?this._store.getGroupById(Y):null;return this._store.dispatch(We(T,!1)),this.passedElement.triggerEvent(rn.highlightItem,{id:T,value:le,label:Me,groupValue:Ve&&Ve.value?Ve.value:null}),this},k.highlightAll=function(){var b=this;return this._store.items.forEach(function(T){return b.highlightItem(T)}),this},k.unhighlightAll=function(){var b=this;return this._store.items.forEach(function(T){return b.unhighlightItem(T)}),this},k.removeActiveItemsByValue=function(b){var T=this;return this._store.activeItems.filter(function(V){return V.value===b}).forEach(function(V){return T._removeItem(V)}),this},k.removeActiveItems=function(b){var T=this;return this._store.activeItems.filter(function(V){var Y=V.id;return Y!==b}).forEach(function(V){return T._removeItem(V)}),this},k.removeHighlightedItems=function(b){var T=this;return b===void 0&&(b=!1),this._store.highlightedActiveItems.forEach(function(V){T._removeItem(V),b&&T._triggerChange(V.value)}),this},k.showDropdown=function(b){var T=this;return this.dropdown.isActive?this:(activeWindow.requestAnimationFrame(function(){T.dropdown.show(),T.containerOuter.open(T.dropdown.distanceFromTopWindow),!b&&T._canSearch&&T.input.focus(),T.passedElement.triggerEvent(rn.showDropdown,{})}),this)},k.hideDropdown=function(b){var T=this;return this.dropdown.isActive?(activeWindow.requestAnimationFrame(function(){T.dropdown.hide(),T.containerOuter.close(),!b&&T._canSearch&&(T.input.removeActiveDescendant(),T.input.blur()),T.passedElement.triggerEvent(rn.hideDropdown,{})}),this):this},k.getValue=function(b){b===void 0&&(b=!1);var T=this._store.activeItems.reduce(function(V,Y){var X=b?Y.value:Y;return V.push(X),V},[]);return this._isSelectOneElement?T[0]:T},k.setValue=function(b){var T=this;return this.initialised?(b.forEach(function(V){return T._setChoiceOrItem(V)}),this):this},k.setChoiceByValue=function(b){var T=this;if(!this.initialised||this._isTextElement)return this;var V=Array.isArray(b)?b:[b];return V.forEach(function(Y){return T._findAndSelectChoiceByValue(Y)}),this},k.setChoices=function(b,T,V,Y){var X=this;if(b===void 0&&(b=[]),T===void 0&&(T=\"value\"),V===void 0&&(V=\"label\"),Y===void 0&&(Y=!1),!this.initialised)throw new ReferenceError(\"setChoices was called on a non-initialized instance of Choices\");if(!this._isSelectElement)throw new TypeError(\"setChoices can't be used with INPUT based Choices\");if(typeof T!=\"string\"||!T)throw new TypeError(\"value parameter must be a name of 'value' field in passed objects\");if(Y&&this.clearChoices(),typeof b==\"function\"){var le=b(this);if(typeof Promise==\"function\"&&le instanceof Promise)return new Promise(function(he){return activeWindow.requestAnimationFrame(he)}).then(function(){return X._handleLoadingState(!0)}).then(function(){return le}).then(function(he){return X.setChoices(he,T,V,Y)}).catch(function(he){X.config.silent||console.error(he)}).then(function(){return X._handleLoadingState(!1)}).then(function(){return X});if(!Array.isArray(le))throw new TypeError(\".setChoices first argument function must return either array of choices or Promise, got: \"+typeof le);return this.setChoices(le,T,V,!1)}if(!Array.isArray(b))throw new TypeError(\".setChoices must be called either with array of choices with a function resulting into Promise of array of choices\");return this.containerOuter.removeLoadingState(),this._startLoading(),b.forEach(function(he){he.choices?X._addGroup({id:parseInt(he.id,10)||null,group:he,valueKey:T,labelKey:V}):X._addChoice({value:he[T],label:he[V],isSelected:he.selected,isDisabled:he.disabled,customProperties:he.customProperties,placeholder:he.placeholder})}),this._stopLoading(),this},k.clearChoices=function(){return this._store.dispatch(nt()),this},k.clearStore=function(){return this._store.dispatch(Hn()),this},k.clearInput=function(){var b=!this._isSelectOneElement;return this.input.clear(b),!this._isTextElement&&this._canSearch&&(this._isSearching=!1,this._store.dispatch(Se(!0))),this},k._render=function(){if(!this._store.isLoading()){this._currentState=this._store.state;var b=this._currentState.choices!==this._prevState.choices||this._currentState.groups!==this._prevState.groups||this._currentState.items!==this._prevState.items,T=this._isSelectElement,V=this._currentState.items!==this._prevState.items;b&&(T&&this._renderChoices(),V&&this._renderItems(),this._prevState=this._currentState)}},k._renderChoices=function(){var b=this,T=this._store,V=T.activeGroups,Y=T.activeChoices,X=document.createDocumentFragment();if(this.choiceList.clear(),this.config.resetScrollPosition&&activeWindow.requestAnimationFrame(function(){return b.choiceList.scrollToTop()}),V.length>=1&&!this._isSearching){var le=Y.filter(function(Qe){return Qe.placeholder===!0&&Qe.groupId===-1});le.length>=1&&(X=this._createChoicesFragment(le,X)),X=this._createGroupsFragment(V,Y,X)}else Y.length>=1&&(X=this._createChoicesFragment(Y,X));if(X.childNodes&&X.childNodes.length>0){var he=this._store.activeItems,Me=this._canAddItem(he,this.input.value);Me.response?(this.choiceList.append(X),this._highlightChoice()):this.choiceList.append(this._getTemplate(\"notice\",Me.notice))}else{var Ve,Le;this._isSearching?(Le=typeof this.config.noResultsText==\"function\"?this.config.noResultsText():this.config.noResultsText,Ve=this._getTemplate(\"notice\",Le,\"no-results\")):(Le=typeof this.config.noChoicesText==\"function\"?this.config.noChoicesText():this.config.noChoicesText,Ve=this._getTemplate(\"notice\",Le,\"no-choices\")),this.choiceList.append(Ve)}},k._renderItems=function(){var b=this._store.activeItems||[];this.itemList.clear();var T=this._createItemsFragment(b);T.childNodes&&this.itemList.append(T)},k._createGroupsFragment=function(b,T,V){var Y=this;V===void 0&&(V=document.createDocumentFragment());var X=function(he){return T.filter(function(Me){return Y._isSelectOneElement?Me.groupId===he.id:Me.groupId===he.id&&(Y.config.renderSelectedChoices===\"always\"||!Me.selected)})};return this.config.shouldSort&&b.sort(this.config.sorter),b.forEach(function(le){var he=X(le);if(he.length>=1){var Me=Y._getTemplate(\"choiceGroup\",le);V.appendChild(Me),Y._createChoicesFragment(he,V,!0)}}),V},k._createChoicesFragment=function(b,T,V){var Y=this;T===void 0&&(T=document.createDocumentFragment()),V===void 0&&(V=!1);var X=this.config,le=X.renderSelectedChoices,he=X.searchResultLimit,Me=X.renderChoiceLimit,Ve=this._isSearching?Wt:this.config.sorter,Le=function(Vn){var ki=le===\"auto\"?Y._isSelectOneElement||!Vn.selected:!0;if(ki){var kr=Y._getTemplate(\"choice\",Vn,Y.config.itemSelectText);T.appendChild(kr)}},Qe=b;le===\"auto\"&&!this._isSelectOneElement&&(Qe=b.filter(function(On){return!On.selected}));var ot=Qe.reduce(function(On,Vn){return Vn.placeholder?On.placeholderChoices.push(Vn):On.normalChoices.push(Vn),On},{placeholderChoices:[],normalChoices:[]}),qe=ot.placeholderChoices,yt=ot.normalChoices;(this.config.shouldSort||this._isSearching)&&yt.sort(Ve);var st=Qe.length,lt=this._isSelectOneElement?[].concat(qe,yt):yt;this._isSearching?st=he:Me&&Me>0&&!V&&(st=Me);for(var qt=0;qt=Y){var he=X?this._searchChoices(b):0;this.passedElement.triggerEvent(rn.search,{value:b,resultCount:he})}else le&&(this._isSearching=!1,this._store.dispatch(Se(!0)))}},k._canAddItem=function(b,T){var V=!0,Y=typeof this.config.addItemText==\"function\"?this.config.addItemText(T):this.config.addItemText;if(!this._isSelectOneElement){var X=Jt(b,T);this.config.maxItemCount>0&&this.config.maxItemCount<=b.length&&(V=!1,Y=typeof this.config.maxItemText==\"function\"?this.config.maxItemText(this.config.maxItemCount):this.config.maxItemText),!this.config.duplicateItemsAllowed&&X&&V&&(V=!1,Y=typeof this.config.uniqueItemText==\"function\"?this.config.uniqueItemText(T):this.config.uniqueItemText),this._isTextElement&&this.config.addItems&&V&&typeof this.config.addItemFilter==\"function\"&&!this.config.addItemFilter(T)&&(V=!1,Y=typeof this.config.customAddItemText==\"function\"?this.config.customAddItemText(T):this.config.customAddItemText)}return{response:V,notice:Y}},k._searchChoices=function(b){var T=typeof b==\"string\"?b.trim():b,V=typeof this._currentValue==\"string\"?this._currentValue.trim():this._currentValue;if(T.length<1&&T===V+\" \")return 0;var Y=this._store.searchableChoices,X=T,le=[].concat(this.config.searchFields),he=Object.assign(this.config.fuseOptions,{keys:le}),Me=new i.a(Y,he),Ve=Me.search(X);return this._currentValue=T,this._highlightPosition=0,this._isSearching=!0,this._store.dispatch(ae(Ve)),Ve.length},k._addEventListeners=function(){var b=document,T=b.documentElement;T.addEventListener(\"touchend\",this._onTouchEnd,!0),this.containerOuter.element.addEventListener(\"keydown\",this._onKeyDown,!0),this.containerOuter.element.addEventListener(\"mousedown\",this._onMouseDown,!0),T.addEventListener(\"click\",this._onClick,{passive:!0}),T.addEventListener(\"touchmove\",this._onTouchMove,{passive:!0}),this.dropdown.element.addEventListener(\"mouseover\",this._onMouseOver,{passive:!0}),this._isSelectOneElement&&(this.containerOuter.element.addEventListener(\"focus\",this._onFocus,{passive:!0}),this.containerOuter.element.addEventListener(\"blur\",this._onBlur,{passive:!0})),this.input.element.addEventListener(\"keyup\",this._onKeyUp,{passive:!0}),this.input.element.addEventListener(\"focus\",this._onFocus,{passive:!0}),this.input.element.addEventListener(\"blur\",this._onBlur,{passive:!0}),this.input.element.form&&this.input.element.form.addEventListener(\"reset\",this._onFormReset,{passive:!0}),this.input.addEventListeners()},k._removeEventListeners=function(){var b=document,T=b.documentElement;T.removeEventListener(\"touchend\",this._onTouchEnd,!0),this.containerOuter.element.removeEventListener(\"keydown\",this._onKeyDown,!0),this.containerOuter.element.removeEventListener(\"mousedown\",this._onMouseDown,!0),T.removeEventListener(\"click\",this._onClick),T.removeEventListener(\"touchmove\",this._onTouchMove),this.dropdown.element.removeEventListener(\"mouseover\",this._onMouseOver),this._isSelectOneElement&&(this.containerOuter.element.removeEventListener(\"focus\",this._onFocus),this.containerOuter.element.removeEventListener(\"blur\",this._onBlur)),this.input.element.removeEventListener(\"keyup\",this._onKeyUp),this.input.element.removeEventListener(\"focus\",this._onFocus),this.input.element.removeEventListener(\"blur\",this._onBlur),this.input.element.form&&this.input.element.form.removeEventListener(\"reset\",this._onFormReset),this.input.removeEventListeners()},k._onKeyDown=function(b){var T,V=b.target,Y=b.keyCode,X=b.ctrlKey,le=b.metaKey,he=this._store.activeItems,Me=this.input.isFocussed,Ve=this.dropdown.isActive,Le=this.itemList.hasChildren(),Qe=String.fromCharCode(Y),ot=Z.BACK_KEY,qe=Z.DELETE_KEY,yt=Z.ENTER_KEY,st=Z.A_KEY,lt=Z.ESC_KEY,qt=Z.UP_KEY,On=Z.DOWN_KEY,Vn=Z.PAGE_UP_KEY,ki=Z.PAGE_DOWN_KEY,kr=X||le;!this._isTextElement&&/[a-zA-Z0-9-_ ]/.test(Qe)&&this.showDropdown();var Jr=(T={},T[st]=this._onAKey,T[yt]=this._onEnterKey,T[lt]=this._onEscapeKey,T[qt]=this._onDirectionKey,T[Vn]=this._onDirectionKey,T[On]=this._onDirectionKey,T[ki]=this._onDirectionKey,T[qe]=this._onDeleteKey,T[ot]=this._onDeleteKey,T);Jr[Y]&&Jr[Y]({event:b,target:V,keyCode:Y,metaKey:le,activeItems:he,hasFocusedInput:Me,hasActiveDropdown:Ve,hasItems:Le,hasCtrlDownKeyPressed:kr})},k._onKeyUp=function(b){var T=b.target,V=b.keyCode,Y=this.input.value,X=this._store.activeItems,le=this._canAddItem(X,Y),he=Z.BACK_KEY,Me=Z.DELETE_KEY;if(this._isTextElement){var Ve=le.notice&&Y;if(Ve){var Le=this._getTemplate(\"notice\",le.notice);this.dropdown.element.innerHTML=Le.outerHTML,this.showDropdown(!0)}else this.hideDropdown(!0)}else{var Qe=(V===he||V===Me)&&!T.value,ot=!this._isTextElement&&this._isSearching,qe=this._canSearch&&le.response;Qe&&ot?(this._isSearching=!1,this._store.dispatch(Se(!0))):qe&&this._handleSearch(this.input.value)}this._canSearch=this.config.searchEnabled},k._onAKey=function(b){var T=b.hasItems,V=b.hasCtrlDownKeyPressed;if(V&&T){this._canSearch=!1;var Y=this.config.removeItems&&!this.input.value&&this.input.element===document.activeElement;Y&&this.highlightAll()}},k._onEnterKey=function(b){var T=b.event,V=b.target,Y=b.activeItems,X=b.hasActiveDropdown,le=Z.ENTER_KEY,he=V.hasAttribute(\"data-button\");if(this._isTextElement&&V.value){var Me=this.input.value,Ve=this._canAddItem(Y,Me);Ve.response&&(this.hideDropdown(!0),this._addItem({value:Me}),this._triggerChange(Me),this.clearInput())}if(he&&(this._handleButtonAction(Y,V),T.preventDefault()),X){var Le=this.dropdown.getChild(\".\"+this.config.classNames.highlightedState);Le&&(Y[0]&&(Y[0].keyCode=le),this._handleChoiceAction(Y,Le)),T.preventDefault()}else this._isSelectOneElement&&(this.showDropdown(),T.preventDefault())},k._onEscapeKey=function(b){var T=b.hasActiveDropdown;T&&(this.hideDropdown(!0),this.containerOuter.focus())},k._onDirectionKey=function(b){var T=b.event,V=b.hasActiveDropdown,Y=b.keyCode,X=b.metaKey,le=Z.DOWN_KEY,he=Z.PAGE_UP_KEY,Me=Z.PAGE_DOWN_KEY;if(V||this._isSelectOneElement){this.showDropdown(),this._canSearch=!1;var Ve=Y===le||Y===Me?1:-1,Le=X||Y===Me||Y===he,Qe=\"[data-choice-selectable]\",ot;if(Le)Ve>0?ot=this.dropdown.element.querySelector(Qe+\":last-of-type\"):ot=this.dropdown.element.querySelector(Qe);else{var qe=this.dropdown.element.querySelector(\".\"+this.config.classNames.highlightedState);qe?ot=Ze(qe,Qe,Ve):ot=this.dropdown.element.querySelector(Qe)}ot&&(It(ot,this.choiceList.element,Ve)||this.choiceList.scrollToChildElement(ot,Ve),this._highlightChoice(ot)),T.preventDefault()}},k._onDeleteKey=function(b){var T=b.event,V=b.target,Y=b.hasFocusedInput,X=b.activeItems;Y&&!V.value&&!this._isSelectOneElement&&(this._handleBackspace(X),T.preventDefault())},k._onTouchMove=function(){this._wasTap&&(this._wasTap=!1)},k._onTouchEnd=function(b){var T=b||b.touches[0],V=T.target,Y=this._wasTap&&this.containerOuter.element.contains(V);if(Y){var X=V===this.containerOuter.element||V===this.containerInner.element;X&&(this._isTextElement?this.input.focus():this._isSelectMultipleElement&&this.showDropdown()),b.stopPropagation()}this._wasTap=!0},k._onMouseDown=function(b){var T=b.target;if(T instanceof HTMLElement){if(Bn&&this.choiceList.element.contains(T)){var V=this.choiceList.element.firstElementChild,Y=this._direction===\"ltr\"?b.offsetX>=V.offsetWidth:b.offsetX0;Y&&this.unhighlightAll(),this.containerOuter.removeFocusState(),this.hideDropdown(!0)}},k._onFocus=function(b){var T=this,V,Y=b.target,X=this.containerOuter.element.contains(Y);if(X){var le=(V={},V[me]=function(){Y===T.input.element&&T.containerOuter.addFocusState()},V[Ie]=function(){T.containerOuter.addFocusState(),Y===T.input.element&&T.showDropdown(!0)},V[at]=function(){Y===T.input.element&&(T.showDropdown(!0),T.containerOuter.addFocusState())},V);le[this.passedElement.element.type]()}},k._onBlur=function(b){var T=this,V=b.target,Y=this.containerOuter.element.contains(V);if(Y&&!this._isScrollingOnIe){var X,le=this._store.activeItems,he=le.some(function(Ve){return Ve.highlighted}),Me=(X={},X[me]=function(){V===T.input.element&&(T.containerOuter.removeFocusState(),he&&T.unhighlightAll(),T.hideDropdown(!0))},X[Ie]=function(){T.containerOuter.removeFocusState(),(V===T.input.element||V===T.containerOuter.element&&!T._canSearch)&&T.hideDropdown(!0)},X[at]=function(){V===T.input.element&&(T.containerOuter.removeFocusState(),T.hideDropdown(!0),he&&T.unhighlightAll())},X);Me[this.passedElement.element.type]()}else this._isScrollingOnIe=!1,this.input.element.focus()},k._onFormReset=function(){this._store.dispatch(Ot(this._initialState))},k._highlightChoice=function(b){var T=this;b===void 0&&(b=null);var V=Array.from(this.dropdown.element.querySelectorAll(\"[data-choice-selectable]\"));if(V.length){var Y=b,X=Array.from(this.dropdown.element.querySelectorAll(\".\"+this.config.classNames.highlightedState));X.forEach(function(le){le.classList.remove(T.config.classNames.highlightedState),le.setAttribute(\"aria-selected\",\"false\")}),Y?this._highlightPosition=V.indexOf(Y):(V.length>this._highlightPosition?Y=V[this._highlightPosition]:Y=V[V.length-1],Y||(Y=V[0])),Y.classList.add(this.config.classNames.highlightedState),Y.setAttribute(\"aria-selected\",\"true\"),this.passedElement.triggerEvent(rn.highlightChoice,{el:Y}),this.dropdown.isActive&&(this.input.setActiveDescendant(Y.id),this.containerOuter.setActiveDescendant(Y.id))}},k._addItem=function(b){var T=b.value,V=b.label,Y=V===void 0?null:V,X=b.choiceId,le=X===void 0?-1:X,he=b.groupId,Me=he===void 0?-1:he,Ve=b.customProperties,Le=Ve===void 0?null:Ve,Qe=b.placeholder,ot=Qe===void 0?!1:Qe,qe=b.keyCode,yt=qe===void 0?null:qe,st=typeof T==\"string\"?T.trim():T,lt=yt,qt=Le,On=this._store.items,Vn=Y||st,ki=le||-1,kr=Me>=0?this._store.getGroupById(Me):null,Jr=On?On.length+1:1;return this.config.prependValue&&(st=this.config.prependValue+st.toString()),this.config.appendValue&&(st+=this.config.appendValue.toString()),this._store.dispatch(Te({value:st,label:Vn,id:Jr,choiceId:ki,groupId:Me,customProperties:Le,placeholder:ot,keyCode:lt})),this._isSelectOneElement&&this.removeActiveItems(Jr),this.passedElement.triggerEvent(rn.addItem,{id:Jr,value:st,label:Vn,customProperties:qt,groupValue:kr&&kr.value?kr.value:void 0,keyCode:lt}),this},k._removeItem=function(b){if(!b||!it(\"Object\",b))return this;var T=b.id,V=b.value,Y=b.label,X=b.choiceId,le=b.groupId,he=le>=0?this._store.getGroupById(le):null;return this._store.dispatch(Ue(T,X)),he&&he.value?this.passedElement.triggerEvent(rn.removeItem,{id:T,value:V,label:Y,groupValue:he.value}):this.passedElement.triggerEvent(rn.removeItem,{id:T,value:V,label:Y}),this},k._addChoice=function(b){var T=b.value,V=b.label,Y=V===void 0?null:V,X=b.isSelected,le=X===void 0?!1:X,he=b.isDisabled,Me=he===void 0?!1:he,Ve=b.groupId,Le=Ve===void 0?-1:Ve,Qe=b.customProperties,ot=Qe===void 0?null:Qe,qe=b.placeholder,yt=qe===void 0?!1:qe,st=b.keyCode,lt=st===void 0?null:st;if(!(typeof T==\"undefined\"||T===null)){var qt=this._store.choices,On=Y||T,Vn=qt?qt.length+1:1,ki=this._baseId+\"-\"+this._idNames.itemChoice+\"-\"+Vn;this._store.dispatch(ce({id:Vn,groupId:Le,elementId:ki,value:T,label:On,disabled:Me,customProperties:ot,placeholder:yt,keyCode:lt})),le&&this._addItem({value:T,label:On,choiceId:Vn,customProperties:ot,placeholder:yt,keyCode:lt})}},k._addGroup=function(b){var T=this,V=b.group,Y=b.id,X=b.valueKey,le=X===void 0?\"value\":X,he=b.labelKey,Me=he===void 0?\"label\":he,Ve=it(\"Object\",V)?V.choices:Array.from(V.getElementsByTagName(\"OPTION\")),Le=Y||Math.floor(new Date().valueOf()*Math.random()),Qe=V.disabled?V.disabled:!1;if(Ve){this._store.dispatch(Ft({value:V.label,id:Le,active:!0,disabled:Qe}));var ot=function(yt){var st=yt.disabled||yt.parentNode&&yt.parentNode.disabled;T._addChoice({value:yt[le],label:it(\"Object\",yt)?yt[Me]:yt.innerHTML,isSelected:yt.selected,isDisabled:st,groupId:Le,customProperties:yt.customProperties,placeholder:yt.placeholder})};Ve.forEach(ot)}else this._store.dispatch(Ft({value:V.label,id:V.id,active:!1,disabled:V.disabled}))},k._getTemplate=function(b){var T;if(!b)return null;for(var V=this.config.classNames,Y=arguments.length,X=new Array(Y>1?Y-1:0),le=1;le{var IH=typeof Element!=\"undefined\",AH=typeof Map==\"function\",OH=typeof Set==\"function\",LH=typeof ArrayBuffer==\"function\"&&!!ArrayBuffer.isView;function jd(e,t){if(e===t)return!0;if(e&&t&&typeof e==\"object\"&&typeof t==\"object\"){if(e.constructor!==t.constructor)return!1;var r,n,i;if(Array.isArray(e)){if(r=e.length,r!=t.length)return!1;for(n=r;n--!==0;)if(!jd(e[n],t[n]))return!1;return!0}var a;if(AH&&e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(a=e.entries();!(n=a.next()).done;)if(!t.has(n.value[0]))return!1;for(a=e.entries();!(n=a.next()).done;)if(!jd(n.value[1],t.get(n.value[0])))return!1;return!0}if(OH&&e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(a=e.entries();!(n=a.next()).done;)if(!t.has(n.value[0]))return!1;return!0}if(LH&&ArrayBuffer.isView(e)&&ArrayBuffer.isView(t)){if(r=e.length,r!=t.length)return!1;for(n=r;n--!==0;)if(e[n]!==t[n])return!1;return!0}if(e.constructor===RegExp)return e.source===t.source&&e.flags===t.flags;if(e.valueOf!==Object.prototype.valueOf&&typeof e.valueOf==\"function\"&&typeof t.valueOf==\"function\")return e.valueOf()===t.valueOf();if(e.toString!==Object.prototype.toString&&typeof e.toString==\"function\"&&typeof t.toString==\"function\")return e.toString()===t.toString();if(i=Object.keys(e),r=i.length,r!==Object.keys(t).length)return!1;for(n=r;n--!==0;)if(!Object.prototype.hasOwnProperty.call(t,i[n]))return!1;if(IH&&e instanceof Element)return!1;for(n=r;n--!==0;)if(!((i[n]===\"_owner\"||i[n]===\"__v\"||i[n]===\"__o\")&&e.$$typeof)&&!jd(e[i[n]],t[i[n]]))return!1;return!0}return e!==e&&t!==t}pC.exports=function(t,r){try{return jd(t,r)}catch(n){if((n.message||\"\").match(/stack|recursion/i))return console.warn(\"react-fast-compare cannot handle circular refs\"),!1;throw n}}});var S_=wn((Gne,Dy)=>{(function(){var e;typeof Dy!=\"undefined\"?e=Dy.exports=n:e=function(){return this||(0,eval)(\"this\")}(),e.format=n,e.vsprintf=r,typeof console!=\"undefined\"&&typeof console.log==\"function\"&&(e.printf=t);function t(){console.log(n.apply(null,arguments))}function r(i,a){return n.apply(null,[i].concat(a))}function n(i){for(var a=1,o=[].slice.call(arguments),s=0,u=i.length,l=\"\",c,d=!1,m,h,g=!1,y,v=function(){return o[a++]},D=function(){for(var I=\"\";/\\d/.test(i[s]);)I+=i[s++],c=i[s];return I.length>0?parseInt(I):null};swf});module.exports=ov(HB);function fu(e,t){let r=Object.keys(t).map(n=>mF(e,n,t[n]));return r.length===1?r[0]:function(){r.forEach(n=>n())}}function mF(e,t,r){let n=e[t],i=e.hasOwnProperty(t),a=r(n);return n&&Object.setPrototypeOf(a,n),Object.setPrototypeOf(o,a),e[t]=o,s;function o(...u){return a===n&&e[t]===o&&s(),a.apply(this,u)}function s(){e[t]===o&&(i?e[t]=n:delete e[t]),a!==n&&(a=n,Object.setPrototypeOf(o,n||Function))}}var Rn=require(\"obsidian\");var Ws,Xe,cv,pF,Va,sv,dv,Hf,Wf,Bf,Vf,fv,Us={},hv=[],gF=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,pu=Array.isArray;function Ii(e,t){for(var r in t)e[r]=t[r];return e}function mv(e){var t=e.parentNode;t&&t.removeChild(e)}function xr(e,t,r){var n,i,a,o={};for(a in t)a==\"key\"?n=t[a]:a==\"ref\"?i=t[a]:o[a]=t[a];if(arguments.length>2&&(o.children=arguments.length>3?Ws.call(arguments,2):r),typeof e==\"function\"&&e.defaultProps!=null)for(a in e.defaultProps)o[a]===void 0&&(o[a]=e.defaultProps[a]);return $s(e,o,n,i,null)}function $s(e,t,r,n,i){var a={type:e,props:t,key:r,ref:n,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,constructor:void 0,__v:i==null?++cv:i,__i:-1,__u:0};return i==null&&Xe.vnode!=null&&Xe.vnode(a),a}function Yf(){return{current:null}}function ct(e){return e.children}function Br(e,t){this.props=e,this.context=t}function $a(e,t){if(t==null)return e.__?$a(e.__,e.__i+1):null;for(var r;tt&&Va.sort(Hf));mu.__r=0}function gv(e,t,r,n,i,a,o,s,u,l,c){var d,m,h,g,y,v=n&&n.__k||hv,D=t.length;for(r.__d=u,yF(r,t,v),u=r.__d,d=0;d0?$s(i.type,i.props,i.key,i.ref?i.ref:null,i.__v):i)!=null?(i.__=e,i.__b=e.__b+1,s=vF(i,r,o,c),i.__i=s,a=null,s!==-1&&(c--,(a=r[s])&&(a.__u|=131072)),a==null||a.__v===null?(s==-1&&d--,typeof i.type!=\"function\"&&(i.__u|=65536)):s!==o&&(s===o+1?d++:s>o?c>u-o?d+=s-o:d--:s(u!=null&&!(131072&u.__u)?1:0))for(;o>=0||s=0){if((u=t[o])&&!(131072&u.__u)&&i==u.key&&a===u.type)return o;o--}if(s2&&(s.children=arguments.length>3?Ws.call(arguments,2):r),$s(e.type,s,n||e.key,i||e.ref,null)}function aa(e,t){var r={__c:t=\"__cC\"+fv++,__:e,Consumer:function(n,i){return n.children(i)},Provider:function(n){var i,a;return this.getChildContext||(i=[],(a={})[t]=this,this.getChildContext=function(){return a},this.shouldComponentUpdate=function(o){this.props.value!==o.value&&i.some(function(s){s.__e=!0,$f(s)})},this.sub=function(o){i.push(o);var s=o.componentWillUnmount;o.componentWillUnmount=function(){i.splice(i.indexOf(o),1),s&&s.call(o)}}),n.children}};return r.Provider.__=r.Consumer.contextType=r}Ws=hv.slice,Xe={__e:function(e,t,r,n){for(var i,a,o;t=t.__;)if((i=t.__c)&&!i.__)try{if((a=i.constructor)&&a.getDerivedStateFromError!=null&&(i.setState(a.getDerivedStateFromError(e)),o=i.__d),i.componentDidCatch!=null&&(i.componentDidCatch(e,n||{}),o=i.__d),o)return i.__E=i}catch(s){e=s}throw e}},cv=0,pF=function(e){return e!=null&&e.constructor==null},Br.prototype.setState=function(e,t){var r;r=this.__s!=null&&this.__s!==this.state?this.__s:this.__s=Ii({},this.state),typeof e==\"function\"&&(e=e(Ii({},r),this.props)),e&&Ii(r,e),e!=null&&this.__v&&(t&&this._sb.push(t),$f(this))},Br.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),$f(this))},Br.prototype.render=ct,Va=[],dv=typeof Promise==\"function\"?Promise.prototype.then.bind(Promise.resolve()):activeWindow.setTimeout,Hf=function(e,t){return e.__v.__b-t.__v.__b},mu.__r=0,Wf=0,Bf=uv(!1),Vf=uv(!0),fv=0;var oa,Qt,qf,bv,Co=0,Mv=[],gu=[],an=Xe,Dv=an.__b,Sv=an.__r,Ev=an.diffed,kv=an.__c,xv=an.unmount,Cv=an.__;function _o(e,t){an.__h&&an.__h(Qt,e,Co||t),Co=0;var r=Qt.__H||(Qt.__H={__:[],__h:[]});return e>=r.__.length&&r.__.push({__V:gu}),r.__[e]}function Ne(e){return Co=1,Jf(Av,e)}function Jf(e,t,r){var n=_o(oa++,2);if(n.t=e,!n.__c&&(n.__=[r?r(t):Av(void 0,t),function(s){var u=n.__N?n.__N[0]:n.__[0],l=n.t(u,s);u!==l&&(n.__N=[l,n.__[1]],n.__c.setState({}))}],n.__c=Qt,!Qt.u)){var i=function(s,u,l){if(!n.__c.__H)return!0;var c=n.__c.__H.__.filter(function(m){return!!m.__c});if(c.every(function(m){return!m.__N}))return!a||a.call(this,s,u,l);var d=!1;return c.forEach(function(m){if(m.__N){var h=m.__[0];m.__=m.__N,m.__N=void 0,h!==m.__[0]&&(d=!0)}}),!(!d&&n.__c.props===s)&&(!a||a.call(this,s,u,l))};Qt.u=!0;var a=Qt.shouldComponentUpdate,o=Qt.componentWillUpdate;Qt.componentWillUpdate=function(s,u,l){if(this.__e){var c=a;a=void 0,i(s,u,l),a=c}o&&o.call(this,s,u,l)},Qt.shouldComponentUpdate=i}return n.__N||n.__}function Ae(e,t){var r=_o(oa++,3);!an.__s&&Zf(r.__H,t)&&(r.__=e,r.i=t,Qt.__H.__h.push(r))}function Ai(e,t){var r=_o(oa++,4);!an.__s&&Zf(r.__H,t)&&(r.__=e,r.i=t,Qt.__h.push(r))}function Fe(e){return Co=5,Re(function(){return{current:e}},[])}function Tv(e,t,r){Co=6,Ai(function(){return typeof e==\"function\"?(e(t()),function(){return e(null)}):e?(e.current=t(),function(){return e.current=null}):void 0},r==null?r:r.concat(e))}function Re(e,t){var r=_o(oa++,7);return Zf(r.__H,t)?(r.__V=e(),r.i=t,r.__h=e,r.__V):r.__}function Ye(e,t){return Co=8,Re(function(){return e},t)}function Ee(e){var t=Qt.context[e.__c],r=_o(oa++,9);return r.c=e,t?(r.__==null&&(r.__=!0,t.sub(Qt)),t.props.value):e.__}function Fv(e,t){an.useDebugValue&&an.useDebugValue(t?t(e):e)}function Iv(){var e=_o(oa++,11);if(!e.__){for(var t=Qt.__v;t!==null&&!t.__m&&t.__!==null;)t=t.__;var r=t.__m||(t.__m=[0,0]);e.__=\"P\"+r[0]+\"-\"+r[1]++}return e.__}function DF(){for(var e;e=Mv.shift();)if(e.__P&&e.__H)try{e.__H.__h.forEach(yu),e.__H.__h.forEach(Gf),e.__H.__h=[]}catch(t){e.__H.__h=[],an.__e(t,e.__v)}}an.__b=function(e){Qt=null,Dv&&Dv(e)},an.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),Cv&&Cv(e,t)},an.__r=function(e){Sv&&Sv(e),oa=0;var t=(Qt=e.__c).__H;t&&(qf===Qt?(t.__h=[],Qt.__h=[],t.__.forEach(function(r){r.__N&&(r.__=r.__N),r.__V=gu,r.__N=r.i=void 0})):(t.__h.forEach(yu),t.__h.forEach(Gf),t.__h=[],oa=0)),qf=Qt},an.diffed=function(e){Ev&&Ev(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&(Mv.push(t)!==1&&bv===an.requestAnimationFrame||((bv=an.requestAnimationFrame)||SF)(DF)),t.__H.__.forEach(function(r){r.i&&(r.__H=r.i),r.__V!==gu&&(r.__=r.__V),r.i=void 0,r.__V=gu})),qf=Qt=null},an.__c=function(e,t){t.some(function(r){try{r.__h.forEach(yu),r.__h=r.__h.filter(function(n){return!n.__||Gf(n)})}catch(n){t.some(function(i){i.__h&&(i.__h=[])}),t=[],an.__e(n,r.__v)}}),kv&&kv(e,t)},an.unmount=function(e){xv&&xv(e);var t,r=e.__c;r&&r.__H&&(r.__H.__.forEach(function(n){try{yu(n)}catch(i){t=i}}),r.__H=void 0,t&&an.__e(t,r.__v))};var _v=typeof activeWindow.requestAnimationFrame==\"function\";function SF(e){var t,r=function(){activeWindow.clearTimeout(n),_v&&activeWindow.cancelAnimationFrame(t),activeWindow.setTimeout(e)},n=activeWindow.setTimeout(r,100);_v&&(t=activeWindow.requestAnimationFrame(r))}function yu(e){var t=Qt,r=e.__c;typeof r==\"function\"&&(e.__c=void 0,r()),Qt=t}function Gf(e){var t=Qt;e.__c=e.__(),Qt=t}function Zf(e,t){return!e||e.length!==t.length||t.some(function(r,n){return r!==e[n]})}function Av(e,t){return typeof t==\"function\"?t(e):t}function $v(e,t){for(var r in t)e[r]=t[r];return e}function Xf(e,t){for(var r in e)if(r!==\"__source\"&&!(r in t))return!0;for(var n in t)if(n!==\"__source\"&&e[n]!==t[n])return!0;return!1}function eh(e,t){this.props=e,this.context=t}function zt(e,t){function r(i){var a=this.props.ref,o=a==i.ref;return!o&&a&&(a.call?a(null):a.current=null),t?!t(this.props,i)||!o:Xf(this.props,i)}function n(i){return this.shouldComponentUpdate=r,xr(e,i)}return n.displayName=\"Memo(\"+(e.displayName||e.name)+\")\",n.prototype.isReactComponent=!0,n.__f=!0,n}(eh.prototype=new Br).isPureReactComponent=!0,eh.prototype.shouldComponentUpdate=function(e,t){return Xf(this.props,e)||Xf(this.state,t)};var Ov=Xe.__b;Xe.__b=function(e){e.type&&e.type.__f&&e.ref&&(e.props.ref=e.ref,e.ref=null),Ov&&Ov(e)};var EF=typeof Symbol!=\"undefined\"&&Symbol.for&&Symbol.for(\"react.forward_ref\")||3911;function kF(e){function t(r){var n=$v({},r);return delete n.ref,e(n,r.ref||null)}return t.$$typeof=EF,t.render=t,t.prototype.isReactComponent=t.__f=!0,t.displayName=\"ForwardRef(\"+(e.displayName||e.name)+\")\",t}var Lv=function(e,t){return e==null?null:fi(fi(e).map(t))},xF={map:Lv,forEach:Lv,count:function(e){return e?fi(e).length:0},only:function(e){var t=fi(e);if(t.length!==1)throw\"Children.only\";return t[0]},toArray:fi},CF=Xe.__e;Xe.__e=function(e,t,r,n){if(e.then){for(var i,a=t;a=a.__;)if((i=a.__c)&&i.__c)return t.__e==null&&(t.__e=r.__e,t.__k=r.__k),i.__c(e,t)}CF(e,t,r,n)};var Pv=Xe.unmount;function Uv(e,t,r){return e&&(e.__c&&e.__c.__H&&(e.__c.__H.__.forEach(function(n){typeof n.__c==\"function\"&&n.__c()}),e.__c.__H=null),(e=$v({},e)).__c!=null&&(e.__c.__P===r&&(e.__c.__P=t),e.__c=null),e.__k=e.__k&&e.__k.map(function(n){return Uv(n,t,r)})),e}function Wv(e,t,r){return e&&r&&(e.__v=null,e.__k=e.__k&&e.__k.map(function(n){return Wv(n,t,r)}),e.__c&&e.__c.__P===t&&(e.__e&&r.appendChild(e.__e),e.__c.__e=!0,e.__c.__P=r)),e}function vu(){this.__u=0,this.t=null,this.__b=null}function Yv(e){var t=e.__.__c;return t&&t.__a&&t.__a(e)}function _F(e){var t,r,n;function i(a){if(t||(t=e()).then(function(o){r=o.default||o},function(o){n=o}),n)throw n;if(!r)throw t;return xr(r,a)}return i.displayName=\"Lazy\",i.__f=!0,i}function Ys(){this.u=null,this.o=null}Xe.unmount=function(e){var t=e.__c;t&&t.__R&&t.__R(),t&&32&e.__u&&(e.type=null),Pv&&Pv(e)},(vu.prototype=new Br).__c=function(e,t){var r=t.__c,n=this;n.t==null&&(n.t=[]),n.t.push(r);var i=Yv(n.__v),a=!1,o=function(){a||(a=!0,r.__R=null,i?i(s):s())};r.__R=o;var s=function(){if(!--n.__u){if(n.state.__a){var u=n.state.__a;n.__v.__k[0]=Wv(u,u.__c.__P,u.__c.__O)}var l;for(n.setState({__a:n.__b=null});l=n.t.pop();)l.forceUpdate()}};n.__u++||32&t.__u||n.setState({__a:n.__b=n.__v.__k[0]}),e.then(o,o)},vu.prototype.componentWillUnmount=function(){this.t=[]},vu.prototype.render=function(e,t){if(this.__b){if(this.__v.__k){var r=document.createElement(\"div\"),n=this.__v.__k[0].__c;this.__v.__k[0]=Uv(this.__b,r,n.__O=n.__P)}this.__b=null}var i=t.__a&&xr(ct,null,e.fallback);return i&&(i.__u&=-33),[xr(ct,null,t.__a?null:e.children),i]};var Nv=function(e,t,r){if(++r[1]===r[0]&&e.o.delete(t),e.props.revealOrder&&(e.props.revealOrder[0]!==\"t\"||!e.o.size))for(r=e.u;r;){for(;r.length>3;)r.pop()();if(r[1]>>1,1),t.i.removeChild(n)}}),xo(xr(MF,{context:t.context},e.__v),t.l)}function Oi(e,t){var r=xr(TF,{__v:e,i:t});return r.containerInfo=t,r}(Ys.prototype=new Br).__a=function(e){var t=this,r=Yv(t.__v),n=t.o.get(e);return n[0]++,function(i){var a=function(){t.props.revealOrder?(n.push(i),Nv(t,e,n)):i()};r?r(a):a()}},Ys.prototype.render=function(e){this.u=null,this.o=new Map;var t=fi(e.children);e.revealOrder&&e.revealOrder[0]===\"b\"&&t.reverse();for(var r=t.length;r--;)this.o.set(t[r],this.u=[1,0,this.u]);return e.children},Ys.prototype.componentDidUpdate=Ys.prototype.componentDidMount=function(){var e=this;this.o.forEach(function(t,r){Nv(e,r,t)})};var zv=typeof Symbol!=\"undefined\"&&Symbol.for&&Symbol.for(\"react.element\")||60103,FF=/^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image(!S)|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/,IF=/^on(Ani|Tra|Tou|BeforeInp|Compo)/,AF=/[A-Z0-9]/g,OF=typeof document!=\"undefined\",LF=function(e){return(typeof Symbol!=\"undefined\"&&typeof Symbol()==\"symbol\"?/fil|che|rad/:/fil|che|ra/).test(e)};function Li(e,t,r){return t.__k==null&&(t.textContent=\"\"),xo(e,t),typeof r==\"function\"&&r(),e?e.__c:null}function PF(e,t,r){return jf(e,t),typeof r==\"function\"&&r(),e?e.__c:null}Br.prototype.isReactComponent={},[\"componentWillMount\",\"componentWillReceiveProps\",\"componentWillUpdate\"].forEach(function(e){Object.defineProperty(Br.prototype,e,{configurable:!0,get:function(){return this[\"UNSAFE_\"+e]},set:function(t){Object.defineProperty(this,e,{configurable:!0,writable:!0,value:t})}})});var Rv=Xe.event;function NF(){}function RF(){return this.cancelBubble}function HF(){return this.defaultPrevented}Xe.event=function(e){return Rv&&(e=Rv(e)),e.persist=NF,e.isPropagationStopped=RF,e.isDefaultPrevented=HF,e.nativeEvent=e};var th,BF={enumerable:!1,configurable:!0,get:function(){return this.class}},Hv=Xe.vnode;Xe.vnode=function(e){typeof e.type==\"string\"&&function(t){var r=t.props,n=t.type,i={};for(var a in r){var o=r[a];if(!(a===\"value\"&&\"defaultValue\"in r&&o==null||OF&&a===\"children\"&&n===\"noscript\"||a===\"class\"||a===\"className\")){var s=a.toLowerCase();a===\"defaultValue\"&&\"value\"in r&&r.value==null?a=\"value\":a===\"download\"&&o===!0?o=\"\":s===\"translate\"&&o===\"no\"?o=!1:s===\"ondoubleclick\"?a=\"ondblclick\":s!==\"onchange\"||n!==\"input\"&&n!==\"textarea\"||LF(r.type)?s===\"onfocus\"?a=\"onfocusin\":s===\"onblur\"?a=\"onfocusout\":IF.test(a)?a=s:n.indexOf(\"-\")===-1&&FF.test(a)?a=a.replace(AF,\"-$&\").toLowerCase():o===null&&(o=void 0):s=a=\"oninput\",s===\"oninput\"&&i[a=s]&&(a=\"oninputCapture\"),i[a]=o}}n==\"select\"&&i.multiple&&Array.isArray(i.value)&&(i.value=fi(r.children).forEach(function(u){u.props.selected=i.value.indexOf(u.props.value)!=-1})),n==\"select\"&&i.defaultValue!=null&&(i.value=fi(r.children).forEach(function(u){u.props.selected=i.multiple?i.defaultValue.indexOf(u.props.value)!=-1:i.defaultValue==u.props.value})),r.class&&!r.className?(i.class=r.class,Object.defineProperty(i,\"className\",BF)):(r.className&&!r.class||r.class&&r.className)&&(i.class=i.className=r.className),t.props=i}(e),e.$$typeof=zv,Hv&&Hv(e)};var Bv=Xe.__r;Xe.__r=function(e){Bv&&Bv(e),th=e.__c};var Vv=Xe.diffed;Xe.diffed=function(e){Vv&&Vv(e);var t=e.props,r=e.__e;r!=null&&e.type===\"textarea\"&&\"value\"in t&&t.value!==r.value&&(r.value=t.value==null?\"\":t.value),th=null};var VF={ReactCurrentDispatcher:{current:{readContext:function(e){return th.__n[e.__c].props.value}}}};function $F(e){return xr.bind(null,e)}function wu(e){return!!e&&e.$$typeof===zv}function UF(e){return wu(e)&&e.type===ct}function WF(e){return!!e&&!!e.displayName&&(typeof e.displayName==\"string\"||e.displayName instanceof String)&&e.displayName.startsWith(\"Memo(\")}function YF(e){return wu(e)?wv.apply(null,arguments):e}function Pi(e){return!!e.__k&&(xo(null,e),!0)}function zF(e){return e&&(e.base||e.nodeType===1&&e)||null}var KF=function(e,t){return e(t)},jF=function(e,t){return e(t)},qF=ct;function Kv(e){e()}function GF(e){return e}function JF(){return[!1,Kv]}var ZF=Ai,QF=wu;function XF(e,t){var r=t(),n=Ne({h:{__:r,v:t}}),i=n[0].h,a=n[1];return Ai(function(){i.__=r,i.v=t,Qf(i)&&a({h:i})},[e,r,t]),Ae(function(){return Qf(i)&&a({h:i}),e(function(){Qf(i)&&a({h:i})})},[e]),r}function Qf(e){var t,r,n=e.v,i=e.__;try{var a=n();return!((t=i)===(r=a)&&(t!==0||1/t==1/r)||t!=t&&r!=r)}catch(o){return!0}}var $e={useState:Ne,useId:Iv,useReducer:Jf,useEffect:Ae,useLayoutEffect:Ai,useInsertionEffect:ZF,useTransition:JF,useDeferredValue:GF,useSyncExternalStore:XF,startTransition:Kv,useRef:Fe,useImperativeHandle:Tv,useMemo:Re,useCallback:Ye,useContext:Ee,useDebugValue:Fv,version:\"17.0.2\",Children:xF,render:Li,hydrate:PF,unmountComponentAtNode:Pi,createPortal:Oi,createElement:xr,createContext:aa,createFactory:$F,cloneElement:YF,createRef:Yf,Fragment:ct,isValidElement:wu,isElement:QF,isFragment:UF,isMemo:WF,findDOMNode:zF,Component:Br,PureComponent:eh,memo:zt,forwardRef:kF,flushSync:jF,unstable_batchedUpdates:KF,StrictMode:qF,Suspense:vu,SuspenseList:Ys,lazy:_F,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:VF};function Ge(e){if(typeof e==\"string\"||typeof e==\"number\")return\"\"+e;let t=\"\";if(Array.isArray(e))for(let r=0,n;rn===i.length-1?r!==t[n]:r===t[n])}function SI(e,t){return e.length===t.length&&e.every((r,n)=>r===t[n])}function js(e,t){if(SI(e,t))return 2;if(!DI(e,t))return 3;let r=e.length-1;return e[r]=0;n--)r={children:{[e[n]]:r}};return r}function qs(e,t){let r=t;for(let n=e.length-2;n>=0;n--)r={children:{[e[n]]:r}};return r}function iw(e,t){let r=t?[e.last(),1,t]:[e.last(),1];return qs(e,{children:{$splice:[r]}})}function aw(e,t,r=0){return qs(e,{children:{$splice:[[e.last()+r,0,...t]]}})}function xI(e,t){return qs(e,{children:{$push:t}})}function CI(e,t){return qs(e,{children:{$unshift:t}})}function Ri(e,t,r,n,i){let a=n?n(un(e,t)):un(e,t),s=js(t,r)===1?-1:0,u=i==null?void 0:i(un(e,t)),l=iw(t,u),c=aw(r,Array.isArray(a)?a:[a],s),d=(0,rw.default)(l,c,{isMergeableObject:h=>Ni(h)||Array.isArray(h)});return(0,la.default)(e,d)}function mi(e,t,r){return(0,la.default)(e,iw(t,r))}function Xr(e,t,r){return(0,la.default)(e,aw(t,r))}function ah(e,t,r){return(0,la.default)(e,xI(t,r))}function ow(e,t,r){return(0,la.default)(e,CI(t,r))}function Du(e,t,r){return(0,la.default)(e,kI(t,r))}function oh(e,t,r){return(0,la.default)(e,qs(t,r))}var Gs=[\"MO\",\"TU\",\"WE\",\"TH\",\"FR\",\"SA\",\"SU\"],Mn=function(){function e(t,r){if(r===0)throw new Error(\"Can't create weekday with n == 0\");this.weekday=t,this.n=r}return e.fromStr=function(t){return new e(Gs.indexOf(t))},e.prototype.nth=function(t){return this.n===t?this:new e(this.weekday,t)},e.prototype.equals=function(t){return this.weekday===t.weekday&&this.n===t.n},e.prototype.toString=function(){var t=Gs[this.weekday];return this.n&&(t=(this.n>0?\"+\":\"\")+String(this.n)+t),t},e.prototype.getJsWeekday=function(){return this.weekday===6?0:this.weekday+1},e}();var Gt=function(e){return e!=null},Cr=function(e){return typeof e==\"number\"},sh=function(e){return typeof e==\"string\"&&Gs.includes(e)},$n=Array.isArray,$r=function(e,t){t===void 0&&(t=e),arguments.length===1&&(t=e,e=0);for(var r=[],n=e;n>0,n.length>t?String(n):(t=t-n.length,t>r.length&&(r+=wt(r,t/r.length)),r.slice(0,t)+String(n))}var lw=function(e,t,r){var n=e.split(t);return r?n.slice(0,r).concat([n.slice(r).join(t)]):n},qn=function(e,t){var r=e%t;return r*t<0?r+t:r},Su=function(e,t){return{div:Math.floor(e/t),mod:qn(e,t)}},_r=function(e){return!Gt(e)||e.length===0},bn=function(e){return!_r(e)},Et=function(e,t){return bn(e)&&e.indexOf(t)!==-1};var pi=function(e,t,r,n,i,a){return n===void 0&&(n=0),i===void 0&&(i=0),a===void 0&&(a=0),new Date(Date.UTC(e,t-1,r,n,i,a))},_I=[31,28,31,30,31,30,31,31,30,31,30,31],cw=1e3*60*60*24,Eu=9999,dw=pi(1970,1,1),MI=[6,0,1,2,3,4,5];var To=function(e){return e%4===0&&e%100!==0||e%400===0},lh=function(e){return e instanceof Date},Ya=function(e){return lh(e)&&!isNaN(e.getTime())};var TI=function(e,t){var r=e.getTime(),n=t.getTime(),i=r-n;return Math.round(i/cw)},Js=function(e){return TI(e,dw)},ku=function(e){return new Date(dw.getTime()+e*cw)},FI=function(e){var t=e.getUTCMonth();return t===1&&To(e.getUTCFullYear())?29:_I[t]},Hi=function(e){return MI[e.getUTCDay()]},uh=function(e,t){var r=pi(e,t+1,1);return[Hi(r),FI(r)]},xu=function(e,t){return t=t||e,new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),t.getHours(),t.getMinutes(),t.getSeconds(),t.getMilliseconds()))},Cu=function(e){var t=new Date(e.getTime());return t},ch=function(e){for(var t=[],r=0;rthis.maxDate;if(this.method===\"between\"){if(r)return!0;if(n)return!1}else if(this.method===\"before\"){if(n)return!1}else if(this.method===\"after\")return r?!0:(this.add(t),!1);return this.add(t)},e.prototype.add=function(t){return this._result.push(t),!0},e.prototype.getValue=function(){var t=this._result;switch(this.method){case\"all\":case\"between\":return t;case\"before\":case\"after\":default:return t.length?t[t.length-1]:null}},e.prototype.clone=function(){return new e(this.method,this.args)},e}(),Vi=II;var dh=function(e,t){return dh=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(r[i]=n[i])},dh(e,t)};function Io(e,t){if(typeof t!=\"function\"&&t!==null)throw new TypeError(\"Class extends value \"+String(t)+\" is not a constructor or null\");dh(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}var Un=function(){return Un=Object.assign||function(t){for(var r,n=1,i=arguments.length;n0&&a[a.length-1])&&(l[0]===6||l[0]===2)){r=0;continue}if(l[0]===3&&(!a||l[1]>a[0]&&l[1]0)&&!(i=n.next()).done;)a.push(i.value)}catch(s){o={error:s}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(o)throw o.error}}return a}function hw(){for(var e=[],t=0;tt[0].length)&&(t=a,r=i)}if(t!=null&&(this.text=this.text.substr(t[0].length),this.text===\"\"&&(this.done=!0)),t==null){this.done=!0,this.symbol=null,this.value=null;return}}while(r===\"SKIP\");return this.symbol=r,this.value=t,!0},e.prototype.accept=function(t){if(this.symbol===t){if(this.value){var r=this.value;return this.nextSymbol(),r}return this.nextSymbol(),!0}return!1},e.prototype.acceptNumber=function(){return this.accept(\"number\")},e.prototype.expect=function(t){if(this.accept(t))return!0;throw new Error(\"expected \"+t+\" but found \"+this.symbol)},e}();function Qs(e,t){t===void 0&&(t=za);var r={},n=new HI(t.tokens);if(!n.start(e))return null;return i(),r;function i(){n.expect(\"every\");var m=n.acceptNumber();if(m&&(r.interval=parseInt(m[0],10)),n.isDone())throw new Error(\"Unexpected end\");switch(n.symbol){case\"day(s)\":r.freq=Oe.DAILY,n.nextSymbol()&&(o(),d());break;case\"weekday(s)\":r.freq=Oe.WEEKLY,r.byweekday=[Oe.MO,Oe.TU,Oe.WE,Oe.TH,Oe.FR],n.nextSymbol(),o(),d();break;case\"week(s)\":r.freq=Oe.WEEKLY,n.nextSymbol()&&(a(),o(),d());break;case\"hour(s)\":r.freq=Oe.HOURLY,n.nextSymbol()&&(a(),d());break;case\"minute(s)\":r.freq=Oe.MINUTELY,n.nextSymbol()&&(a(),d());break;case\"month(s)\":r.freq=Oe.MONTHLY,n.nextSymbol()&&(a(),d());break;case\"year(s)\":r.freq=Oe.YEARLY,n.nextSymbol()&&(a(),d());break;case\"monday\":case\"tuesday\":case\"wednesday\":case\"thursday\":case\"friday\":case\"saturday\":case\"sunday\":r.freq=Oe.WEEKLY;var h=n.symbol.substr(0,2).toUpperCase();if(r.byweekday=[Oe[h]],!n.nextSymbol())return;for(;n.accept(\"comma\");){if(n.isDone())throw new Error(\"Unexpected end\");var g=u();if(!g)throw new Error(\"Unexpected symbol \"+n.symbol+\", expected weekday\");r.byweekday.push(Oe[g]),n.nextSymbol()}o(),c(),d();break;case\"january\":case\"february\":case\"march\":case\"april\":case\"may\":case\"june\":case\"july\":case\"august\":case\"september\":case\"october\":case\"november\":case\"december\":if(r.freq=Oe.YEARLY,r.bymonth=[s()],!n.nextSymbol())return;for(;n.accept(\"comma\");){if(n.isDone())throw new Error(\"Unexpected end\");var y=s();if(!y)throw new Error(\"Unexpected symbol \"+n.symbol+\", expected month\");r.bymonth.push(y),n.nextSymbol()}a(),d();break;default:throw new Error(\"Unknown symbol\")}}function a(){var m=n.accept(\"on\"),h=n.accept(\"the\");if(m||h)do{var g=l(),y=u(),v=s();if(g)y?(n.nextSymbol(),r.byweekday||(r.byweekday=[]),r.byweekday.push(Oe[y].nth(g))):(r.bymonthday||(r.bymonthday=[]),r.bymonthday.push(g),n.accept(\"day(s)\"));else if(y)n.nextSymbol(),r.byweekday||(r.byweekday=[]),r.byweekday.push(Oe[y]);else if(n.symbol===\"weekday(s)\")n.nextSymbol(),r.byweekday||(r.byweekday=[Oe.MO,Oe.TU,Oe.WE,Oe.TH,Oe.FR]);else if(n.symbol===\"week(s)\"){n.nextSymbol();var D=n.acceptNumber();if(!D)throw new Error(\"Unexpected symbol \"+n.symbol+\", expected week number\");for(r.byweekno=[parseInt(D[0],10)];n.accept(\"comma\");){if(D=n.acceptNumber(),!D)throw new Error(\"Unexpected symbol \"+n.symbol+\"; expected monthday\");r.byweekno.push(parseInt(D[0],10))}}else if(v)n.nextSymbol(),r.bymonth||(r.bymonth=[]),r.bymonth.push(v);else return}while(n.accept(\"comma\")||n.accept(\"the\")||n.accept(\"on\"))}function o(){var m=n.accept(\"at\");if(m)do{var h=n.acceptNumber();if(!h)throw new Error(\"Unexpected symbol \"+n.symbol+\", expected hour\");for(r.byhour=[parseInt(h[0],10)];n.accept(\"comma\");){if(h=n.acceptNumber(),!h)throw new Error(\"Unexpected symbol \"+n.symbol+\"; expected hour\");r.byhour.push(parseInt(h[0],10))}}while(n.accept(\"comma\")||n.accept(\"at\"))}function s(){switch(n.symbol){case\"january\":return 1;case\"february\":return 2;case\"march\":return 3;case\"april\":return 4;case\"may\":return 5;case\"june\":return 6;case\"july\":return 7;case\"august\":return 8;case\"september\":return 9;case\"october\":return 10;case\"november\":return 11;case\"december\":return 12;default:return!1}}function u(){switch(n.symbol){case\"monday\":case\"tuesday\":case\"wednesday\":case\"thursday\":case\"friday\":case\"saturday\":case\"sunday\":return n.symbol.substr(0,2).toUpperCase();default:return!1}}function l(){switch(n.symbol){case\"last\":return n.nextSymbol(),-1;case\"first\":return n.nextSymbol(),1;case\"second\":return n.nextSymbol(),n.accept(\"last\")?-2:2;case\"third\":return n.nextSymbol(),n.accept(\"last\")?-3:3;case\"nth\":var m=parseInt(n.value[1],10);if(m<-366||m>366)throw new Error(\"Nth out of range: \"+m);return n.nextSymbol(),n.accept(\"last\")?-m:m;default:return!1}}function c(){n.accept(\"on\"),n.accept(\"the\");var m=l();if(m)for(r.bymonthday=[m],n.nextSymbol();n.accept(\"comma\");){if(m=l(),!m)throw new Error(\"Unexpected symbol \"+n.symbol+\"; expected monthday\");r.bymonthday.push(m),n.nextSymbol()}}function d(){if(n.symbol===\"until\"){var m=Date.parse(n.text);if(!m)throw new Error(\"Cannot parse until date:\"+n.text);r.until=new Date(m)}else n.accept(\"for\")&&(r.count=parseInt(n.value[0],10),n.expect(\"number\"))}}var pt;(function(e){e[e.YEARLY=0]=\"YEARLY\",e[e.MONTHLY=1]=\"MONTHLY\",e[e.WEEKLY=2]=\"WEEKLY\",e[e.DAILY=3]=\"DAILY\",e[e.HOURLY=4]=\"HOURLY\",e[e.MINUTELY=5]=\"MINUTELY\",e[e.SECONDLY=6]=\"SECONDLY\"})(pt||(pt={}));function Xs(e){return e12){var n=Math.floor(this.month/12),i=qn(this.month,12);this.month=i,this.year+=n,this.month===0&&(this.month=12,--this.year)}},t.prototype.addWeekly=function(r,n){n>this.getWeekday()?this.day+=-(this.getWeekday()+1+(6-n))+r*7:this.day+=-(this.getWeekday()-n)+r*7,this.fixDay()},t.prototype.addDaily=function(r){this.day+=r,this.fixDay()},t.prototype.addHours=function(r,n,i){for(n&&(this.hour+=Math.floor((23-this.hour)/r)*r);;){this.hour+=r;var a=Su(this.hour,24),o=a.div,s=a.mod;if(o&&(this.hour=s,this.addDaily(o)),_r(i)||Et(i,this.hour))break}},t.prototype.addMinutes=function(r,n,i,a){for(n&&(this.minute+=Math.floor((1439-(this.hour*60+this.minute))/r)*r);;){this.minute+=r;var o=Su(this.minute,60),s=o.div,u=o.mod;if(s&&(this.minute=u,this.addHours(s,!1,i)),(_r(i)||Et(i,this.hour))&&(_r(a)||Et(a,this.minute)))break}},t.prototype.addSeconds=function(r,n,i,a,o){for(n&&(this.second+=Math.floor((86399-(this.hour*3600+this.minute*60+this.second))/r)*r);;){this.second+=r;var s=Su(this.second,60),u=s.div,l=s.mod;if(u&&(this.second=l,this.addMinutes(u,!1,i,a)),(_r(i)||Et(i,this.hour))&&(_r(a)||Et(a,this.minute))&&(_r(o)||Et(o,this.second)))break}},t.prototype.fixDay=function(){if(!(this.day<=28)){var r=uh(this.year,this.month-1)[1];if(!(this.day<=r))for(;this.day>r;){if(this.day-=r,++this.month,this.month===13&&(this.month=1,++this.year,this.year>Eu))return;r=uh(this.year,this.month-1)[1]}}},t.prototype.add=function(r,n){var i=r.freq,a=r.interval,o=r.wkst,s=r.byhour,u=r.byminute,l=r.bysecond;switch(i){case pt.YEARLY:return this.addYears(a);case pt.MONTHLY:return this.addMonths(a);case pt.WEEKLY:return this.addWeekly(a,o);case pt.DAILY:return this.addDaily(a);case pt.HOURLY:return this.addHours(a,n,s);case pt.MINUTELY:return this.addMinutes(a,n,s,u);case pt.SECONDLY:return this.addSeconds(a,n,s,u,l)}},t}(Po);function hh(e){for(var t=[],r=Object.keys(e),n=0,i=r;n=-366&&n<=366))throw new Error(\"bysetpos must be between 1 and 366, or between -366 and -1\")}}if(!(t.byweekno||bn(t.byweekno)||bn(t.byyearday)||t.bymonthday||bn(t.bymonthday)||Gt(t.byweekday)||Gt(t.byeaster)))switch(t.freq){case Oe.YEARLY:t.bymonth||(t.bymonth=t.dtstart.getUTCMonth()+1),t.bymonthday=t.dtstart.getUTCDate();break;case Oe.MONTHLY:t.bymonthday=t.dtstart.getUTCDate();break;case Oe.WEEKLY:t.byweekday=[Hi(t.dtstart)];break}if(Gt(t.bymonth)&&!$n(t.bymonth)&&(t.bymonth=[t.bymonth]),Gt(t.byyearday)&&!$n(t.byyearday)&&Cr(t.byyearday)&&(t.byyearday=[t.byyearday]),!Gt(t.bymonthday))t.bymonthday=[],t.bynmonthday=[];else if($n(t.bymonthday)){for(var i=[],a=[],r=0;r0?i.push(n):n<0&&a.push(n)}t.bymonthday=i,t.bynmonthday=a}else t.bymonthday<0?(t.bynmonthday=[t.bymonthday],t.bymonthday=[]):(t.bynmonthday=[],t.bymonthday=[t.bymonthday]);if(Gt(t.byweekno)&&!$n(t.byweekno)&&(t.byweekno=[t.byweekno]),!Gt(t.byweekday))t.bynweekday=null;else if(Cr(t.byweekday))t.byweekday=[t.byweekday],t.bynweekday=null;else if(sh(t.byweekday))t.byweekday=[Mn.fromStr(t.byweekday).weekday],t.bynweekday=null;else if(t.byweekday instanceof Mn)!t.byweekday.n||t.freq>Oe.MONTHLY?(t.byweekday=[t.byweekday.weekday],t.bynweekday=null):(t.bynweekday=[[t.byweekday.weekday,t.byweekday.n]],t.byweekday=null);else{for(var o=[],s=[],r=0;rOe.MONTHLY?o.push(u.weekday):s.push([u.weekday,u.n])}t.byweekday=bn(o)?o:null,t.bynweekday=bn(s)?s:null}return Gt(t.byhour)?Cr(t.byhour)&&(t.byhour=[t.byhour]):t.byhour=t.freq=4?(c=0,l=s.yearlen+qn(o-t.wkst,7)):l=n-c;for(var d=Math.floor(l/7),m=qn(l,7),h=Math.floor(d+m/4),g=0;g0&&y<=h){var v=void 0;y>1?(v=c+(y-1)*7,c!==u&&(v-=7-u)):v=c;for(var D=0;D<7&&(s.wnomask[v]=1,v++,s.wdaymask[v]!==t.wkst);D++);}}if(Et(t.byweekno,1)){var v=c+h*7;if(c!==u&&(v-=7-u),v=4?(x=0,A=O+qn(C-t.wkst,7)):A=n-c,I=Math.floor(52+qn(A,7)/4)}if(Et(t.byweekno,I))for(var v=0;va)return $i(e);if(I>=r){var C=Hw(I,t);if(!e.accept(C)||s&&(--s,!s))return $i(e)}}else for(var D=h;Da)return $i(e);if(I>=r){var C=Hw(I,t);if(!e.accept(C)||s&&(--s,!s))return $i(e)}}}if(t.interval===0||(u.add(t,y),u.year>Eu))return $i(e);Xs(n)||(c=l.gettimeset(n)(u.hour,u.minute,u.second,0)),l.rebuild(u.year,u.month)}}function JI(e,t,r){var n=r.bymonth,i=r.byweekno,a=r.byweekday,o=r.byeaster,s=r.bymonthday,u=r.bynmonthday,l=r.byyearday;return bn(n)&&!Et(n,e.mmask[t])||bn(i)&&!e.wnomask[t]||bn(a)&&!Et(a,e.wdaymask[t])||bn(e.nwdaymask)&&!e.nwdaymask[t]||o!==null&&!Et(e.eastermask,t)||(bn(s)||bn(u))&&!Et(s,e.mdaymask[t])&&!Et(u,e.nmdaymask[t])||bn(l)&&(t=e.yearlen&&!Et(l,t+1-e.yearlen)&&!Et(l,-e.nextyearlen+t-e.yearlen))}function Hw(e,t){return new Ka(e,t.tzid).rezonedDate()}function $i(e){return e.getValue()}function ZI(e,t,r,n,i){for(var a=!1,o=t;o=Oe.HOURLY&&bn(i)&&!Et(i,t.hour)||n>=Oe.MINUTELY&&bn(a)&&!Et(a,t.minute)||n>=Oe.SECONDLY&&bn(o)&&!Et(o,t.second)?[]:e.gettimeset(n)(t.hour,t.minute,t.second,t.millisecond)}var Ur={MO:new Mn(0),TU:new Mn(1),WE:new Mn(2),TH:new Mn(3),FR:new Mn(4),SA:new Mn(5),SU:new Mn(6)},el={freq:pt.YEARLY,dtstart:null,interval:1,wkst:Ur.MO,count:null,until:null,tzid:null,bysetpos:null,bymonth:null,bymonthday:null,bynmonthday:null,byyearday:null,byweekno:null,byweekday:null,bynweekday:null,byhour:null,byminute:null,bysecond:null,byeaster:null},Dw=Object.keys(el),Oe=function(){function e(t,r){t===void 0&&(t={}),r===void 0&&(r=!1),this._cache=r?null:new kw,this.origOptions=hh(t);var n=ww(t).parsedOptions;this.options=n}return e.parseText=function(t,r){return Qs(t,r)},e.fromText=function(t,r){return pw(t,r)},e.fromString=function(t){return new e(e.parseString(t)||void 0)},e.prototype._iter=function(t){return _u(t,this.options)},e.prototype._cacheGet=function(t,r){return this._cache?this._cache._cacheGet(t,r):!1},e.prototype._cacheAdd=function(t,r,n){if(this._cache)return this._cache._cacheAdd(t,r,n)},e.prototype.all=function(t){if(t)return this._iter(new fh(\"all\",{},t));var r=this._cacheGet(\"all\");return r===!1&&(r=this._iter(new Vi(\"all\",{})),this._cacheAdd(\"all\",r)),r},e.prototype.between=function(t,r,n,i){if(n===void 0&&(n=!1),!Ya(t)||!Ya(r))throw new Error(\"Invalid date passed in to RRule.between\");var a={before:r,after:t,inc:n};if(i)return this._iter(new fh(\"between\",a,i));var o=this._cacheGet(\"between\",a);return o===!1&&(o=this._iter(new Vi(\"between\",a)),this._cacheAdd(\"between\",o,a)),o},e.prototype.before=function(t,r){if(r===void 0&&(r=!1),!Ya(t))throw new Error(\"Invalid date passed in to RRule.before\");var n={dt:t,inc:r},i=this._cacheGet(\"before\",n);return i===!1&&(i=this._iter(new Vi(\"before\",n)),this._cacheAdd(\"before\",i,n)),i},e.prototype.after=function(t,r){if(r===void 0&&(r=!1),!Ya(t))throw new Error(\"Invalid date passed in to RRule.after\");var n={dt:t,inc:r},i=this._cacheGet(\"after\",n);return i===!1&&(i=this._iter(new Vi(\"after\",n)),this._cacheAdd(\"after\",i,n)),i},e.prototype.count=function(){return this.all().length},e.prototype.toString=function(){return rl(this.origOptions)},e.prototype.toText=function(t,r,n){return gw(this,t,r,n)},e.prototype.isFullyConvertibleToText=function(){return yw(this)},e.prototype.clone=function(){return new e(this.origOptions)},e.FREQUENCIES=[\"YEARLY\",\"MONTHLY\",\"WEEKLY\",\"DAILY\",\"HOURLY\",\"MINUTELY\",\"SECONDLY\"],e.YEARLY=pt.YEARLY,e.MONTHLY=pt.MONTHLY,e.WEEKLY=pt.WEEKLY,e.DAILY=pt.DAILY,e.HOURLY=pt.HOURLY,e.MINUTELY=pt.MINUTELY,e.SECONDLY=pt.SECONDLY,e.MO=Ur.MO,e.TU=Ur.TU,e.WE=Ur.WE,e.TH=Ur.TH,e.FR=Ur.FR,e.SA=Ur.SA,e.SU=Ur.SU,e.parseString=nl,e.optionsToString=rl,e}();function Bw(e,t,r,n,i,a){var o={},s=e.accept;function u(m,h){r.forEach(function(g){g.between(m,h,!0).forEach(function(y){o[Number(y)]=!0})})}i.forEach(function(m){var h=new Ka(m,a).rezonedDate();o[Number(h)]=!0}),e.accept=function(m){var h=Number(m);return isNaN(h)?s.call(this,m):!o[h]&&(u(new Date(h-1),new Date(h+1)),!o[h])?(o[h]=!0,s.call(this,m)):!0},e.method===\"between\"&&(u(e.args.after,e.args.before),e.accept=function(m){var h=Number(m);return o[h]?!0:(o[h]=!0,s.call(this,m))});for(var l=0;l1||i.length||a.length||o.length){var c=new gh(l);return c.dtstart(s),c.tzid(u||void 0),n.forEach(function(m){c.rrule(new Oe(ph(m,s,u),l))}),i.forEach(function(m){c.rdate(m)}),a.forEach(function(m){c.exrule(new Oe(ph(m,s,u),l))}),o.forEach(function(m){c.exdate(m)}),t.compatible&&t.dtstart&&c.rdate(s),c}var d=n[0]||{};return new Oe(ph(d,d.dtstart||t.dtstart||s,d.tzid||t.tzid||u),l)}function Mu(e,t){return t===void 0&&(t={}),eA(e,tA(t))}function ph(e,t,r){return Un(Un({},e),{dtstart:t,tzid:r})}function tA(e){var t=[],r=Object.keys(e),n=Object.keys(Vw);if(r.forEach(function(i){Et(n,i)||t.push(i)}),t.length)throw new Error(\"Invalid options: \"+t.join(\", \"));return Un(Un({},Vw),e)}function nA(e){if(e.indexOf(\":\")===-1)return{name:\"RRULE\",value:e};var t=lw(e,\":\",1),r=t[0],n=t[1];return{name:r,value:n}}function rA(e){var t=nA(e),r=t.name,n=t.value,i=r.split(\";\");if(!i)throw new Error(\"empty property name\");return{name:i[0].toUpperCase(),parms:i.slice(1),value:n}}function iA(e,t){if(t===void 0&&(t=!1),e=e&&e.trim(),!e)throw new Error(\"Invalid empty string\");if(!t)return e.split(/\\s/);for(var r=e.split(`\n`),n=0;n0&&i[0]===\" \"?(r[n-1]+=i.slice(1),r.splice(n,1)):n+=1:r.splice(n,1)}return r}function aA(e){e.forEach(function(t){if(!/(VALUE=DATE(-TIME)?)|(TZID=)/.test(t))throw new Error(\"unsupported RDATE/EXDATE parm: \"+t)})}function $w(e,t){return aA(t),e.split(\",\").map(function(r){return Zs(r)})}function Uw(e){var t=this;return function(r){if(r!==void 0&&(t[\"_\".concat(e)]=r),t[\"_\".concat(e)]!==void 0)return t[\"_\".concat(e)];for(var n=0;n