7754 lines
321 KiB
HTML
7754 lines
321 KiB
HTML
|
||
<!doctype html>
|
||
<html lang="en" class="no-js">
|
||
<head>
|
||
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
|
||
<meta name="description" content="Build Power. Not Rent It. Own your digital infrastructure.">
|
||
|
||
|
||
<meta name="author" content="Bunker Operations">
|
||
|
||
|
||
<link rel="canonical" href="https://bnkserve.org/v2/backend/modules/shifts/">
|
||
|
||
|
||
<link rel="prev" href="../locations/">
|
||
|
||
|
||
<link rel="next" href="../canvass/">
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="icon" href="../../../../assets/favicon.png">
|
||
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.1">
|
||
|
||
|
||
|
||
<title>Shifts Module - Changemaker Lite</title>
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="../../../../assets/stylesheets/main.484c7ddc.min.css">
|
||
|
||
|
||
<link rel="stylesheet" href="../../../../assets/stylesheets/palette.ab4e12ef.min.css">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
|
||
<style>:root{--md-text-font:"Inter";--md-code-font:"JetBrains Mono"}</style>
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="../../../../stylesheets/extra.css">
|
||
|
||
<link rel="stylesheet" href="../../../../stylesheets/home.css">
|
||
|
||
<link rel="stylesheet" href="../../../../assets/css/video-player.css">
|
||
|
||
<script>__md_scope=new URL("../../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<meta property="og:type" content="website" />
|
||
<meta property="og:title" content="Shifts Module - Changemaker Lite" />
|
||
<meta property="og:description" content="Build Power. Not Rent It. Own your digital infrastructure." />
|
||
<meta property="og:image" content="https://bnkserve.org/assets/images/social/v2/backend/modules/shifts.png" />
|
||
<meta property="og:image:type" content="image/png" />
|
||
<meta property="og:image:width" content="1200" />
|
||
<meta property="og:image:height" content="630" />
|
||
<meta property="og:url" content="https://bnkserve.org/v2/backend/modules/shifts/" />
|
||
<meta property="twitter:card" content="summary_large_image" />
|
||
<meta property="twitter:title" content="Shifts Module - Changemaker Lite" />
|
||
<meta property="twitter:description" content="Build Power. Not Rent It. Own your digital infrastructure." />
|
||
<meta property="twitter:image" content="https://bnkserve.org/assets/images/social/v2/backend/modules/shifts.png" />
|
||
</head>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="amber">
|
||
|
||
|
||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||
<label class="md-overlay" for="__drawer"></label>
|
||
<div data-md-component="skip">
|
||
|
||
|
||
<a href="#shifts-module" class="md-skip">
|
||
Skip to content
|
||
</a>
|
||
|
||
</div>
|
||
<div data-md-component="announce">
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||
|
||
<img src="../../../../assets/logo.png" alt="logo">
|
||
|
||
</a>
|
||
<label class="md-header__button md-icon" for="__drawer">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||
</label>
|
||
<div class="md-header__title" data-md-component="header-title">
|
||
<div class="md-header__ellipsis">
|
||
<div class="md-header__topic">
|
||
<span class="md-ellipsis">
|
||
Changemaker Lite
|
||
</span>
|
||
</div>
|
||
<div class="md-header__topic" data-md-component="header-topic">
|
||
<span class="md-ellipsis">
|
||
|
||
Shifts Module
|
||
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<form class="md-header__option" data-md-component="palette">
|
||
|
||
|
||
|
||
|
||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="amber" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_0">
|
||
|
||
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3zm3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95zm-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31"/></svg>
|
||
</label>
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="deep-purple" data-md-color-accent="amber" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
|
||
|
||
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_0" hidden>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5s-1.65.15-2.39.42zM3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29zm.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14zM20.65 7l-1.77 3.79a7.02 7.02 0 0 0-2.38-4.15zm-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29zM12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44z"/></svg>
|
||
</label>
|
||
|
||
|
||
</form>
|
||
|
||
|
||
|
||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-header__button md-icon" for="__search">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||
</label>
|
||
<div class="md-search" data-md-component="search" role="dialog">
|
||
<label class="md-search__overlay" for="__search"></label>
|
||
<div class="md-search__inner" role="search">
|
||
<form class="md-search__form" name="search">
|
||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||
<label class="md-search__icon md-icon" for="__search">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
|
||
</label>
|
||
<nav class="md-search__options" aria-label="Search">
|
||
|
||
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
|
||
</a>
|
||
|
||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
||
</button>
|
||
</nav>
|
||
|
||
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
||
|
||
</form>
|
||
<div class="md-search__output">
|
||
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
|
||
<div class="md-search-result" data-md-component="search-result">
|
||
<div class="md-search-result__meta">
|
||
Initializing search
|
||
</div>
|
||
<ol class="md-search-result__list" role="presentation"></ol>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="md-header__source">
|
||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||
<div class="md-source__icon md-icon">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||
</div>
|
||
<div class="md-source__repository">
|
||
changemaker.lite
|
||
</div>
|
||
</a>
|
||
</div>
|
||
|
||
</nav>
|
||
|
||
|
||
|
||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||
<div class="md-grid">
|
||
<ul class="md-tabs__list">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item">
|
||
<a href="../../../.." class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
Home
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item md-tabs__item--active">
|
||
<a href="../../../" class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
V2 Documentation
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item">
|
||
<a href="../../../../phil/" class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
Philosophy
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item">
|
||
<a href="../../../../v1/" class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
V1 Documentation (Legacy)
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item">
|
||
<a href="../../../../blog/" class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
Blog
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</div>
|
||
</nav>
|
||
|
||
|
||
</header>
|
||
|
||
<div class="md-container" data-md-component="container">
|
||
|
||
|
||
|
||
|
||
<main class="md-main" data-md-component="main">
|
||
<div class="md-main__inner md-grid">
|
||
|
||
|
||
|
||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||
<div class="md-sidebar__scrollwrap">
|
||
<div class="md-sidebar__inner">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
|
||
<label class="md-nav__title" for="__drawer">
|
||
<a href="../../../.." title="Changemaker Lite" class="md-nav__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||
|
||
<img src="../../../../assets/logo.png" alt="logo">
|
||
|
||
</a>
|
||
Changemaker Lite
|
||
</label>
|
||
|
||
<div class="md-nav__source">
|
||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||
<div class="md-source__icon md-icon">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||
</div>
|
||
<div class="md-source__repository">
|
||
changemaker.lite
|
||
</div>
|
||
</a>
|
||
</div>
|
||
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../.." class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Home
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" checked>
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
V2 Documentation
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2" id="__nav_2_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="true">
|
||
<label class="md-nav__title" for="__nav_2">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
V2 Documentation
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_2" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../getting-started/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Getting Started
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_2" id="__nav_2_2_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_2_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_2">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Getting Started
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../getting-started/quick-start/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Quick Start
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_3" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../architecture/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Architecture
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_3" id="__nav_2_3_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_3_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_3">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Architecture
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../architecture/dual-api/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Dual API System
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../architecture/authentication/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Authentication & Security
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_4" checked>
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Backend
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_4" id="__nav_2_4_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_4_label" aria-expanded="true">
|
||
<label class="md-nav__title" for="__nav_2_4">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Backend
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_4_2" checked>
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Modules
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_4_2" id="__nav_2_4_2_label" tabindex="0">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="3" aria-labelledby="__nav_2_4_2_label" aria-expanded="true">
|
||
<label class="md-nav__title" for="__nav_2_4_2">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Modules
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../auth/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Auth Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../users/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Users Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../settings/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Settings Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../campaigns/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Campaigns Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../representatives/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Representatives Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../responses/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Responses Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../locations/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Locations Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--active">
|
||
|
||
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Shifts Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
<a href="./" class="md-nav__link md-nav__link--active">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Shifts Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
|
||
|
||
|
||
<nav class="md-nav md-nav--secondary" aria-label="On this page">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__title" for="__toc">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
On this page
|
||
</label>
|
||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#overview" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Overview
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#file-paths" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
File Paths
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#database-models" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Database Models
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#api-endpoints" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
API Endpoints
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="API Endpoints">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#admin-endpoints-authentication-required" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin Endpoints (Authentication Required)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#volunteer-endpoints-authentication-required" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Volunteer Endpoints (Authentication Required)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-endpoints-no-authentication" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public Endpoints (No Authentication)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#admin-endpoint-details" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin Endpoint Details
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Admin Endpoint Details">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshifts" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshiftsstats" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts/stats
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshiftsid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts/:id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#post-apimapshifts" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
POST /api/map/shifts
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#put-apimapshiftsid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
PUT /api/map/shifts/:id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#delete-apimapshiftsid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DELETE /api/map/shifts/:id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#post-apimapshiftsidsignups" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
POST /api/map/shifts/:id/signups
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#delete-apimapshiftsidsignupssignupid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DELETE /api/map/shifts/:id/signups/:signupId
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#post-apimapshiftsidemail-details" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
POST /api/map/shifts/:id/email-details
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#volunteer-endpoint-details" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Volunteer Endpoint Details
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Volunteer Endpoint Details">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshiftsvolunteerupcoming" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts/volunteer/upcoming
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshiftsvolunteermy-signups" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts/volunteer/my-signups
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#post-apimapshiftsvolunteeridsignup" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
POST /api/map/shifts/volunteer/:id/signup
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#delete-apimapshiftsvolunteeridsignup" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DELETE /api/map/shifts/volunteer/:id/signup
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-endpoint-details" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public Endpoint Details
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Public Endpoint Details">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshiftspublic" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts/public
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#post-apimapshiftspublicidsignup" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
POST /api/map/shifts/public/:id/signup
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#service-functions" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Service Functions
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Service Functions">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsservicefindallfilters" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.findAll(filters)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsservicefindbyidid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.findById(id)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsservicecreatedata-userid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.create(data, userId)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsserviceupdateid-data" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.update(id, data)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsserviceaddsignupshiftid-data" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.addSignup(shiftId, data)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsservicepublicsignupshiftid-data" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.publicSignup(shiftId, data)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsserviceremovesignupsignupid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.removeSignup(signupId)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsserviceemailshiftdetailsshiftid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.emailShiftDetails(shiftId)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#validation-schemas" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Validation Schemas
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Validation Schemas">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#create-shift-schema" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Create Shift Schema
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#update-shift-schema" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Update Shift Schema
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-signup-schema" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public Signup Schema
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#code-examples" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Code Examples
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Code Examples">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#admin-create-shift-with-cut-association" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin: Create Shift with Cut Association
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#volunteer-sign-up-for-shift" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Volunteer: Sign Up for Shift
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-sign-up-without-account" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public: Sign Up Without Account
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#admin-email-all-volunteers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin: Email All Volunteers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#frontend-integration" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Frontend Integration
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#performance-considerations" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Performance Considerations
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Performance Considerations">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#capacity-tracking" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Capacity Tracking
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#unique-constraint-performance" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Unique Constraint Performance
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#rate-limiting" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Rate Limiting
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#troubleshooting" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Troubleshooting
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Troubleshooting">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shift-status-not-updating-automatically" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Shift Status Not Updating Automatically
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duplicate-signups" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Duplicate Signups
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#confirmation-emails-not-sending" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Confirmation Emails Not Sending
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#temp-users-not-expiring" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Temp Users Not Expiring
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#rate-limit-too-strict" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Rate Limit Too Strict
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#related-documentation" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Related Documentation
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../canvass/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Canvass Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../pages/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Pages Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../media/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Media Module
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../services/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Services
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../middleware/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Middleware
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../utilities/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Utilities
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_5" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../frontend/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Frontend
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_5" id="__nav_2_5_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_5_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_5">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Frontend
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../frontend/components/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Components
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../frontend/layouts/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Layouts
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../frontend/pages/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Pages
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_6" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../database/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Database
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_6" id="__nav_2_6_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_6_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_6">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Database
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../database/schema/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Schema Overview
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../database/migrations/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Migrations
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../database/seeding/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Seeding
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../database/indexes/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Indexes
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../database/models/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Models
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_7" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../features/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Features
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_7" id="__nav_2_7_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_7_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_7">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Features
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../features/influence/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Influence
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../features/map/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Map
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../features/landing-pages/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Landing Pages
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../features/email-templates/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Email Templates
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../features/media/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Media
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../features/newsletter/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Newsletter
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../features/observability/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Observability
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../features/tunnel/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Tunnel
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_8" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../deployment/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Deployment
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_8" id="__nav_2_8_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_8_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_8">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Deployment
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../deployment/docker-compose/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Docker Compose
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../deployment/environment-variables/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Environment Variables
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../deployment/nginx/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Nginx Configuration
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../deployment/ssl-tls/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
SSL/TLS
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../deployment/tunneling/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Tunneling
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../deployment/monitoring-stack/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Monitoring Stack
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../deployment/healthchecks/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Health Checks
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../deployment/scaling/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Scaling
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../deployment/backup-restore/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Backup & Restore
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_9" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../development/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Development
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_9" id="__nav_2_9_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_9_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_9">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Development
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../development/local-setup/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Local Setup
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../development/docker-workflow/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Docker Workflow
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../development/git-workflow/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Git Workflow
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../development/npm-commands/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
NPM Commands
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../development/migrations/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Migrations
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../development/typescript/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
TypeScript
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../development/testing/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Testing
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../development/debugging/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Debugging
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../development/code-style/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Code Style
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_10" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../api-reference/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
API Reference
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_10_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_10">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
API Reference
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_11" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../user-guides/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
User Guides
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_11" id="__nav_2_11_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_11_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_11">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
User Guides
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../user-guides/admin-guide/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Admin Guide
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../user-guides/campaign-manager-guide/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Campaign Manager Guide
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../user-guides/map-organizer-guide/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Map Organizer Guide
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../user-guides/content-editor-guide/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Content Editor Guide
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../user-guides/volunteer-guide/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Volunteer Guide
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_12" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../troubleshooting/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Troubleshooting
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_12" id="__nav_2_12_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_12_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_12">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Troubleshooting
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../troubleshooting/faq/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
FAQ
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../troubleshooting/common-errors/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Common Errors
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../troubleshooting/auth-issues/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Auth Issues
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../troubleshooting/database-issues/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Database Issues
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../troubleshooting/docker-issues/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Docker Issues
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../troubleshooting/email-issues/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Email Issues
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../troubleshooting/geocoding-issues/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Geocoding Issues
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../troubleshooting/monitoring-issues/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Monitoring Issues
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../troubleshooting/performance-optimization/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Performance Optimization
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_13" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../migration/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Migration
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_13" id="__nav_2_13_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_13_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_13">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Migration
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../migration/feature-parity/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Feature Parity
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../migration/breaking-changes/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Breaking Changes
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../migration/api-changes/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
API Changes
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../migration/data-migration/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Data Migration
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_14" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../contributing/" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Contributing
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_14" id="__nav_2_14_label" tabindex="">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_14_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_2_14">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Contributing
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../contributing/development-setup/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Development Setup
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../contributing/code-of-conduct/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Code of Conduct
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../contributing/pull-requests/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Pull Requests
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../../contributing/roadmap/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Roadmap
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../../phil/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Philosophy
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../../v1/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
V1 Documentation (Legacy)
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../../blog/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Blog
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||
<div class="md-sidebar__scrollwrap">
|
||
<div class="md-sidebar__inner">
|
||
|
||
|
||
|
||
|
||
<nav class="md-nav md-nav--secondary" aria-label="On this page">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__title" for="__toc">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
On this page
|
||
</label>
|
||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#overview" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Overview
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#file-paths" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
File Paths
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#database-models" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Database Models
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#api-endpoints" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
API Endpoints
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="API Endpoints">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#admin-endpoints-authentication-required" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin Endpoints (Authentication Required)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#volunteer-endpoints-authentication-required" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Volunteer Endpoints (Authentication Required)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-endpoints-no-authentication" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public Endpoints (No Authentication)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#admin-endpoint-details" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin Endpoint Details
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Admin Endpoint Details">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshifts" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshiftsstats" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts/stats
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshiftsid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts/:id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#post-apimapshifts" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
POST /api/map/shifts
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#put-apimapshiftsid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
PUT /api/map/shifts/:id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#delete-apimapshiftsid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DELETE /api/map/shifts/:id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#post-apimapshiftsidsignups" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
POST /api/map/shifts/:id/signups
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#delete-apimapshiftsidsignupssignupid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DELETE /api/map/shifts/:id/signups/:signupId
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#post-apimapshiftsidemail-details" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
POST /api/map/shifts/:id/email-details
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#volunteer-endpoint-details" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Volunteer Endpoint Details
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Volunteer Endpoint Details">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshiftsvolunteerupcoming" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts/volunteer/upcoming
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshiftsvolunteermy-signups" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts/volunteer/my-signups
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#post-apimapshiftsvolunteeridsignup" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
POST /api/map/shifts/volunteer/:id/signup
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#delete-apimapshiftsvolunteeridsignup" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DELETE /api/map/shifts/volunteer/:id/signup
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-endpoint-details" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public Endpoint Details
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Public Endpoint Details">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#get-apimapshiftspublic" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GET /api/map/shifts/public
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#post-apimapshiftspublicidsignup" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
POST /api/map/shifts/public/:id/signup
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#service-functions" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Service Functions
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Service Functions">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsservicefindallfilters" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.findAll(filters)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsservicefindbyidid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.findById(id)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsservicecreatedata-userid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.create(data, userId)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsserviceupdateid-data" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.update(id, data)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsserviceaddsignupshiftid-data" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.addSignup(shiftId, data)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsservicepublicsignupshiftid-data" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.publicSignup(shiftId, data)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsserviceremovesignupsignupid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.removeSignup(signupId)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shiftsserviceemailshiftdetailsshiftid" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
shiftsService.emailShiftDetails(shiftId)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#validation-schemas" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Validation Schemas
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Validation Schemas">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#create-shift-schema" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Create Shift Schema
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#update-shift-schema" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Update Shift Schema
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-signup-schema" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public Signup Schema
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#code-examples" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Code Examples
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Code Examples">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#admin-create-shift-with-cut-association" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin: Create Shift with Cut Association
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#volunteer-sign-up-for-shift" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Volunteer: Sign Up for Shift
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-sign-up-without-account" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public: Sign Up Without Account
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#admin-email-all-volunteers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin: Email All Volunteers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#frontend-integration" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Frontend Integration
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#performance-considerations" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Performance Considerations
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Performance Considerations">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#capacity-tracking" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Capacity Tracking
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#unique-constraint-performance" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Unique Constraint Performance
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#rate-limiting" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Rate Limiting
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#troubleshooting" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Troubleshooting
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Troubleshooting">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#shift-status-not-updating-automatically" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Shift Status Not Updating Automatically
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duplicate-signups" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Duplicate Signups
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#confirmation-emails-not-sending" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Confirmation Emails Not Sending
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#temp-users-not-expiring" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Temp Users Not Expiring
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#rate-limit-too-strict" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Rate Limit Too Strict
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#related-documentation" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Related Documentation
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="md-content" data-md-component="content">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<nav class="md-path" aria-label="Navigation" >
|
||
<ol class="md-path__list">
|
||
|
||
|
||
|
||
|
||
<li class="md-path__item">
|
||
<a href="../../../.." class="md-path__link">
|
||
|
||
<span class="md-ellipsis">
|
||
Home
|
||
</span>
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-path__item">
|
||
<a href="../../../" class="md-path__link">
|
||
|
||
<span class="md-ellipsis">
|
||
V2 Documentation
|
||
</span>
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-path__item">
|
||
<a href="../../" class="md-path__link">
|
||
|
||
<span class="md-ellipsis">
|
||
Backend
|
||
</span>
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-path__item">
|
||
<a href="../" class="md-path__link">
|
||
|
||
<span class="md-ellipsis">
|
||
Modules
|
||
</span>
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ol>
|
||
</nav>
|
||
|
||
|
||
<article class="md-content__inner md-typeset">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="https://gitea.bnkops.com/admin/changemaker.lite/src/branch/main/mkdocs/docs/v2/backend/modules/shifts.md" title="Edit this page" class="md-content__button md-icon" rel="edit">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 20H6V4h7v5h5v3.1l2-2V8l-6-6H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4zm10.2-7c.1 0 .3.1.4.2l1.3 1.3c.2.2.2.6 0 .8l-1 1-2.1-2.1 1-1c.1-.1.2-.2.4-.2m0 3.9L14.1 23H12v-2.1l6.1-6.1z"/></svg>
|
||
</a>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="https://gitea.bnkops.com/admin/changemaker.lite/src/branch/main/mkdocs/docs/v2/backend/modules/shifts.md" title="View source of this page" class="md-content__button md-icon">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 18c.56 0 1 .44 1 1s-.44 1-1 1-1-.44-1-1 .44-1 1-1m0-3c-2.73 0-5.06 1.66-6 4 .94 2.34 3.27 4 6 4s5.06-1.66 6-4c-.94-2.34-3.27-4-6-4m0 6.5a2.5 2.5 0 0 1-2.5-2.5 2.5 2.5 0 0 1 2.5-2.5 2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5M9.27 20H6V4h7v5h5v4.07c.7.08 1.36.25 2 .49V8l-6-6H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h4.5a8.2 8.2 0 0 1-1.23-2"/></svg>
|
||
</a>
|
||
|
||
|
||
|
||
<h1 id="shifts-module">Shifts Module<a class="headerlink" href="#shifts-module" title="Permanent link">¶</a></h1>
|
||
<h2 id="overview">Overview<a class="headerlink" href="#overview" title="Permanent link">¶</a></h2>
|
||
<p>The Shifts module manages volunteer shift scheduling with public signup capabilities. It provides comprehensive CRUD operations for shift management, volunteer signup tracking, and automatic status updates based on capacity. The module includes three separate routers for admin management, authenticated volunteer portal access, and public signup flows.</p>
|
||
<p><strong>Key Features:</strong></p>
|
||
<ul>
|
||
<li>Full shift CRUD with pagination, search, and filtering</li>
|
||
<li>Automatic status management (OPEN → FULL based on capacity)</li>
|
||
<li>Cut association for canvassing shifts (optional)</li>
|
||
<li>Three signup sources: admin-added, authenticated user, public</li>
|
||
<li>Temporary user creation for public signups (auto-expires after shift date)</li>
|
||
<li>Email confirmation system with readable passwords for new users</li>
|
||
<li>Capacity tracking (currentVolunteers / maxVolunteers)</li>
|
||
<li>Cancellation system with capacity recalculation</li>
|
||
<li>Email all volunteers functionality</li>
|
||
<li>Rate limiting on signup endpoints (5/min per IP)</li>
|
||
<li>Prometheus metrics tracking (<code>cm_shift_signups_total</code>)</li>
|
||
</ul>
|
||
<h2 id="file-paths">File Paths<a class="headerlink" href="#file-paths" title="Permanent link">¶</a></h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>File</th>
|
||
<th>Purpose</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>api/src/modules/map/shifts/shifts.routes.ts</code></td>
|
||
<td>3 routers: admin, volunteer, public (242 lines)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>api/src/modules/map/shifts/shifts.service.ts</code></td>
|
||
<td>Shift business logic with signup flows (754 lines)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>api/src/modules/map/shifts/shifts.schemas.ts</code></td>
|
||
<td>Zod validation schemas (55 lines)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="database-models">Database Models<a class="headerlink" href="#database-models" title="Permanent link">¶</a></h2>
|
||
<div class="language-text highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>model Shift {
|
||
</span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a> id String @id @default(cuid())
|
||
</span><span id="__span-0-3"><a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a> title String
|
||
</span><span id="__span-0-4"><a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a> description String? @db.Text
|
||
</span><span id="__span-0-5"><a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> date DateTime @db.Date
|
||
</span><span id="__span-0-6"><a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> startTime String // HH:MM format
|
||
</span><span id="__span-0-7"><a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> endTime String // HH:MM format
|
||
</span><span id="__span-0-8"><a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> location String?
|
||
</span><span id="__span-0-9"><a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> maxVolunteers Int
|
||
</span><span id="__span-0-10"><a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> currentVolunteers Int @default(0)
|
||
</span><span id="__span-0-11"><a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> status ShiftStatus @default(OPEN)
|
||
</span><span id="__span-0-12"><a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> isPublic Boolean @default(false)
|
||
</span><span id="__span-0-13"><a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> cutId String?
|
||
</span><span id="__span-0-14"><a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> cut Cut? @relation(fields: [cutId], references: [id], onDelete: SetNull)
|
||
</span><span id="__span-0-15"><a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> createdBy String?
|
||
</span><span id="__span-0-16"><a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> createdAt DateTime @default(now())
|
||
</span><span id="__span-0-17"><a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> updatedAt DateTime @updatedAt
|
||
</span><span id="__span-0-18"><a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a>
|
||
</span><span id="__span-0-19"><a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> signups ShiftSignup[]
|
||
</span><span id="__span-0-20"><a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> canvassVisits CanvassVisit[]
|
||
</span><span id="__span-0-21"><a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> canvassSessions CanvassSession[]
|
||
</span><span id="__span-0-22"><a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a>
|
||
</span><span id="__span-0-23"><a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> @@index([cutId])
|
||
</span><span id="__span-0-24"><a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> @@map("shifts")
|
||
</span><span id="__span-0-25"><a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a>}
|
||
</span><span id="__span-0-26"><a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a>
|
||
</span><span id="__span-0-27"><a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>enum ShiftStatus {
|
||
</span><span id="__span-0-28"><a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> OPEN // Accepting signups
|
||
</span><span id="__span-0-29"><a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> FULL // Max capacity reached
|
||
</span><span id="__span-0-30"><a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> CANCELLED // Shift cancelled
|
||
</span><span id="__span-0-31"><a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a>}
|
||
</span><span id="__span-0-32"><a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a>
|
||
</span><span id="__span-0-33"><a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a>model ShiftSignup {
|
||
</span><span id="__span-0-34"><a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> id String @id @default(cuid())
|
||
</span><span id="__span-0-35"><a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> shiftId String
|
||
</span><span id="__span-0-36"><a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> shift Shift @relation(fields: [shiftId], references: [id], onDelete: Cascade)
|
||
</span><span id="__span-0-37"><a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> shiftTitle String?
|
||
</span><span id="__span-0-38"><a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> userId String?
|
||
</span><span id="__span-0-39"><a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
||
</span><span id="__span-0-40"><a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> userEmail String
|
||
</span><span id="__span-0-41"><a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> userName String?
|
||
</span><span id="__span-0-42"><a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> userPhone String?
|
||
</span><span id="__span-0-43"><a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> signupDate DateTime @default(now())
|
||
</span><span id="__span-0-44"><a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> status SignupStatus @default(CONFIRMED)
|
||
</span><span id="__span-0-45"><a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> signupSource SignupSource @default(AUTHENTICATED)
|
||
</span><span id="__span-0-46"><a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a>
|
||
</span><span id="__span-0-47"><a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> @@unique([shiftId, userEmail])
|
||
</span><span id="__span-0-48"><a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> @@index([shiftId])
|
||
</span><span id="__span-0-49"><a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> @@map("shift_signups")
|
||
</span><span id="__span-0-50"><a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a>}
|
||
</span><span id="__span-0-51"><a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a>
|
||
</span><span id="__span-0-52"><a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a>enum SignupStatus {
|
||
</span><span id="__span-0-53"><a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> CONFIRMED // Active signup
|
||
</span><span id="__span-0-54"><a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> CANCELLED // Cancelled (can be re-activated)
|
||
</span><span id="__span-0-55"><a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a>}
|
||
</span><span id="__span-0-56"><a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a>
|
||
</span><span id="__span-0-57"><a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a>enum SignupSource {
|
||
</span><span id="__span-0-58"><a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> AUTHENTICATED // Logged-in user signup
|
||
</span><span id="__span-0-59"><a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> PUBLIC // Anonymous public signup
|
||
</span><span id="__span-0-60"><a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a> ADMIN // Added by admin
|
||
</span><span id="__span-0-61"><a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a>}
|
||
</span></code></pre></div>
|
||
<p><strong>Key Relationships:</strong></p>
|
||
<ul>
|
||
<li><strong>Shift → ShiftSignup</strong>: One-to-many (cascade delete)</li>
|
||
<li><strong>Shift → Cut</strong>: Optional many-to-one (cut assignment for canvassing, set null on delete)</li>
|
||
<li><strong>Shift → CanvassSession/CanvassVisit</strong>: One-to-many (canvassing data linked to shifts)</li>
|
||
<li><strong>ShiftSignup → User</strong>: Optional many-to-one (set null on user delete, preserves signup record)</li>
|
||
</ul>
|
||
<p><strong>Unique Constraints:</strong></p>
|
||
<ul>
|
||
<li><code>[shiftId, userEmail]</code> — One signup per email per shift (allows re-activation of cancelled signups)</li>
|
||
</ul>
|
||
<h2 id="api-endpoints">API Endpoints<a class="headerlink" href="#api-endpoints" title="Permanent link">¶</a></h2>
|
||
<h3 id="admin-endpoints-authentication-required">Admin Endpoints (Authentication Required)<a class="headerlink" href="#admin-endpoints-authentication-required" title="Permanent link">¶</a></h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Method</th>
|
||
<th>Path</th>
|
||
<th>Auth</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>GET</td>
|
||
<td><code>/api/map/shifts</code></td>
|
||
<td>MAP_ADMIN</td>
|
||
<td>List paginated shifts</td>
|
||
</tr>
|
||
<tr>
|
||
<td>GET</td>
|
||
<td><code>/api/map/shifts/stats</code></td>
|
||
<td>MAP_ADMIN</td>
|
||
<td>Shift statistics</td>
|
||
</tr>
|
||
<tr>
|
||
<td>GET</td>
|
||
<td><code>/api/map/shifts/:id</code></td>
|
||
<td>MAP_ADMIN</td>
|
||
<td>Get shift with signups</td>
|
||
</tr>
|
||
<tr>
|
||
<td>POST</td>
|
||
<td><code>/api/map/shifts</code></td>
|
||
<td>MAP_ADMIN</td>
|
||
<td>Create shift</td>
|
||
</tr>
|
||
<tr>
|
||
<td>PUT</td>
|
||
<td><code>/api/map/shifts/:id</code></td>
|
||
<td>MAP_ADMIN</td>
|
||
<td>Update shift</td>
|
||
</tr>
|
||
<tr>
|
||
<td>DELETE</td>
|
||
<td><code>/api/map/shifts/:id</code></td>
|
||
<td>MAP_ADMIN</td>
|
||
<td>Delete shift</td>
|
||
</tr>
|
||
<tr>
|
||
<td>POST</td>
|
||
<td><code>/api/map/shifts/:id/signups</code></td>
|
||
<td>MAP_ADMIN</td>
|
||
<td>Add volunteer signup</td>
|
||
</tr>
|
||
<tr>
|
||
<td>DELETE</td>
|
||
<td><code>/api/map/shifts/:id/signups/:signupId</code></td>
|
||
<td>MAP_ADMIN</td>
|
||
<td>Remove signup</td>
|
||
</tr>
|
||
<tr>
|
||
<td>POST</td>
|
||
<td><code>/api/map/shifts/:id/email-details</code></td>
|
||
<td>MAP_ADMIN</td>
|
||
<td>Email all volunteers</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Admin Roles:</strong> <code>SUPER_ADMIN</code>, <code>MAP_ADMIN</code></p>
|
||
<h3 id="volunteer-endpoints-authentication-required">Volunteer Endpoints (Authentication Required)<a class="headerlink" href="#volunteer-endpoints-authentication-required" title="Permanent link">¶</a></h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Method</th>
|
||
<th>Path</th>
|
||
<th>Auth</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>GET</td>
|
||
<td><code>/api/map/shifts/volunteer/upcoming</code></td>
|
||
<td>Any logged-in</td>
|
||
<td>Upcoming shifts with signup status</td>
|
||
</tr>
|
||
<tr>
|
||
<td>GET</td>
|
||
<td><code>/api/map/shifts/volunteer/my-signups</code></td>
|
||
<td>Any logged-in</td>
|
||
<td>Own confirmed signups</td>
|
||
</tr>
|
||
<tr>
|
||
<td>POST</td>
|
||
<td><code>/api/map/shifts/volunteer/:id/signup</code></td>
|
||
<td>Any logged-in</td>
|
||
<td>Sign up for shift</td>
|
||
</tr>
|
||
<tr>
|
||
<td>DELETE</td>
|
||
<td><code>/api/map/shifts/volunteer/:id/signup</code></td>
|
||
<td>Any logged-in</td>
|
||
<td>Cancel own signup</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="public-endpoints-no-authentication">Public Endpoints (No Authentication)<a class="headerlink" href="#public-endpoints-no-authentication" title="Permanent link">¶</a></h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Method</th>
|
||
<th>Path</th>
|
||
<th>Auth</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>GET</td>
|
||
<td><code>/api/map/shifts/public</code></td>
|
||
<td>None</td>
|
||
<td>List public upcoming shifts</td>
|
||
</tr>
|
||
<tr>
|
||
<td>POST</td>
|
||
<td><code>/api/map/shifts/public/:id/signup</code></td>
|
||
<td>None</td>
|
||
<td>Public signup (creates temp user if needed)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="admin-endpoint-details">Admin Endpoint Details<a class="headerlink" href="#admin-endpoint-details" title="Permanent link">¶</a></h2>
|
||
<h3 id="get-apimapshifts">GET /api/map/shifts<a class="headerlink" href="#get-apimapshifts" title="Permanent link">¶</a></h3>
|
||
<p>List shifts with pagination, search, and filtering.</p>
|
||
<p><strong>Query Parameters:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Parameter</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>page</td>
|
||
<td>number</td>
|
||
<td>No</td>
|
||
<td>1</td>
|
||
<td>Page number</td>
|
||
</tr>
|
||
<tr>
|
||
<td>limit</td>
|
||
<td>number</td>
|
||
<td>No</td>
|
||
<td>20</td>
|
||
<td>Results per page (max 100)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>search</td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td>-</td>
|
||
<td>Search title or location</td>
|
||
</tr>
|
||
<tr>
|
||
<td>status</td>
|
||
<td>ShiftStatus</td>
|
||
<td>No</td>
|
||
<td>-</td>
|
||
<td>Filter by status</td>
|
||
</tr>
|
||
<tr>
|
||
<td>upcoming</td>
|
||
<td>boolean</td>
|
||
<td>No</td>
|
||
<td>-</td>
|
||
<td>Filter to shifts with date >= today</td>
|
||
</tr>
|
||
<tr>
|
||
<td>sortBy</td>
|
||
<td>enum</td>
|
||
<td>No</td>
|
||
<td><code>date</code></td>
|
||
<td>Sort field: <code>date</code>, <code>createdAt</code>, <code>title</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>sortOrder</td>
|
||
<td>enum</td>
|
||
<td>No</td>
|
||
<td><code>desc</code></td>
|
||
<td>Sort direction: <code>asc</code>, <code>desc</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Example Request:</strong></p>
|
||
<div class="language-bash highlight"><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>curl<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Authorization: Bearer <token>"</span><span class="w"> </span><span class="se">\</span>
|
||
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a><span class="w"> </span><span class="s2">"http://api.cmlite.org/api/map/shifts?upcoming=true&status=OPEN&page=1&limit=10"</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (200 OK):</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="p">{</span>
|
||
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="w"> </span><span class="nt">"shifts"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-2-4"><a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-2-5"><a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a><span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-2-6"><a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a><span class="w"> </span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Canvassing residential areas in Ward 5. Meet at campaign office."</span><span class="p">,</span>
|
||
</span><span id="__span-2-7"><a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a><span class="w"> </span><span class="nt">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-15T00:00:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-2-8"><a id="__codelineno-2-8" name="__codelineno-2-8" href="#__codelineno-2-8"></a><span class="w"> </span><span class="nt">"startTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10:00"</span><span class="p">,</span>
|
||
</span><span id="__span-2-9"><a id="__codelineno-2-9" name="__codelineno-2-9" href="#__codelineno-2-9"></a><span class="w"> </span><span class="nt">"endTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"14:00"</span><span class="p">,</span>
|
||
</span><span id="__span-2-10"><a id="__codelineno-2-10" name="__codelineno-2-10" href="#__codelineno-2-10"></a><span class="w"> </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123 Main St (Campaign Office)"</span><span class="p">,</span>
|
||
</span><span id="__span-2-11"><a id="__codelineno-2-11" name="__codelineno-2-11" href="#__codelineno-2-11"></a><span class="w"> </span><span class="nt">"maxVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="p">,</span>
|
||
</span><span id="__span-2-12"><a id="__codelineno-2-12" name="__codelineno-2-12" href="#__codelineno-2-12"></a><span class="w"> </span><span class="nt">"currentVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span>
|
||
</span><span id="__span-2-13"><a id="__codelineno-2-13" name="__codelineno-2-13" href="#__codelineno-2-13"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OPEN"</span><span class="p">,</span>
|
||
</span><span id="__span-2-14"><a id="__codelineno-2-14" name="__codelineno-2-14" href="#__codelineno-2-14"></a><span class="w"> </span><span class="nt">"isPublic"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
</span><span id="__span-2-15"><a id="__codelineno-2-15" name="__codelineno-2-15" href="#__codelineno-2-15"></a><span class="w"> </span><span class="nt">"cutId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx0987654321"</span><span class="p">,</span>
|
||
</span><span id="__span-2-16"><a id="__codelineno-2-16" name="__codelineno-2-16" href="#__codelineno-2-16"></a><span class="w"> </span><span class="nt">"cut"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-2-17"><a id="__codelineno-2-17" name="__codelineno-2-17" href="#__codelineno-2-17"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx0987654321"</span><span class="p">,</span>
|
||
</span><span id="__span-2-18"><a id="__codelineno-2-18" name="__codelineno-2-18" href="#__codelineno-2-18"></a><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ward 5 Residential"</span>
|
||
</span><span id="__span-2-19"><a id="__codelineno-2-19" name="__codelineno-2-19" href="#__codelineno-2-19"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-2-20"><a id="__codelineno-2-20" name="__codelineno-2-20" href="#__codelineno-2-20"></a><span class="w"> </span><span class="nt">"createdBy"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx1111111111"</span><span class="p">,</span>
|
||
</span><span id="__span-2-21"><a id="__codelineno-2-21" name="__codelineno-2-21" href="#__codelineno-2-21"></a><span class="w"> </span><span class="nt">"createdAt"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-01T12:00:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-2-22"><a id="__codelineno-2-22" name="__codelineno-2-22" href="#__codelineno-2-22"></a><span class="w"> </span><span class="nt">"updatedAt"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-11T14:30:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-2-23"><a id="__codelineno-2-23" name="__codelineno-2-23" href="#__codelineno-2-23"></a><span class="w"> </span><span class="nt">"_count"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-2-24"><a id="__codelineno-2-24" name="__codelineno-2-24" href="#__codelineno-2-24"></a><span class="w"> </span><span class="nt">"signups"</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span>
|
||
</span><span id="__span-2-25"><a id="__codelineno-2-25" name="__codelineno-2-25" href="#__codelineno-2-25"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-2-26"><a id="__codelineno-2-26" name="__codelineno-2-26" href="#__codelineno-2-26"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-2-27"><a id="__codelineno-2-27" name="__codelineno-2-27" href="#__codelineno-2-27"></a><span class="w"> </span><span class="p">],</span>
|
||
</span><span id="__span-2-28"><a id="__codelineno-2-28" name="__codelineno-2-28" href="#__codelineno-2-28"></a><span class="w"> </span><span class="nt">"pagination"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-2-29"><a id="__codelineno-2-29" name="__codelineno-2-29" href="#__codelineno-2-29"></a><span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
</span><span id="__span-2-30"><a id="__codelineno-2-30" name="__codelineno-2-30" href="#__codelineno-2-30"></a><span class="w"> </span><span class="nt">"limit"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span>
|
||
</span><span id="__span-2-31"><a id="__codelineno-2-31" name="__codelineno-2-31" href="#__codelineno-2-31"></a><span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">23</span><span class="p">,</span>
|
||
</span><span id="__span-2-32"><a id="__codelineno-2-32" name="__codelineno-2-32" href="#__codelineno-2-32"></a><span class="w"> </span><span class="nt">"totalPages"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span>
|
||
</span><span id="__span-2-33"><a id="__codelineno-2-33" name="__codelineno-2-33" href="#__codelineno-2-33"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-2-34"><a id="__codelineno-2-34" name="__codelineno-2-34" href="#__codelineno-2-34"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="get-apimapshiftsstats">GET /api/map/shifts/stats<a class="headerlink" href="#get-apimapshiftsstats" title="Permanent link">¶</a></h3>
|
||
<p>Get shift statistics.</p>
|
||
<p><strong>Example Request:</strong></p>
|
||
<div class="language-bash highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a>curl<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Authorization: Bearer <token>"</span><span class="w"> </span><span class="se">\</span>
|
||
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="w"> </span><span class="s2">"http://api.cmlite.org/api/map/shifts/stats"</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (200 OK):</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="p">{</span>
|
||
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a><span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">45</span><span class="p">,</span>
|
||
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="w"> </span><span class="nt">"open"</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="p">,</span>
|
||
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a><span class="w"> </span><span class="nt">"full"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span>
|
||
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a><span class="w"> </span><span class="nt">"cancelled"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span>
|
||
</span><span id="__span-4-6"><a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a><span class="w"> </span><span class="nt">"upcoming"</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="p">,</span>
|
||
</span><span id="__span-4-7"><a id="__codelineno-4-7" name="__codelineno-4-7" href="#__codelineno-4-7"></a><span class="w"> </span><span class="nt">"totalSignups"</span><span class="p">:</span><span class="w"> </span><span class="mi">287</span>
|
||
</span><span id="__span-4-8"><a id="__codelineno-4-8" name="__codelineno-4-8" href="#__codelineno-4-8"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="get-apimapshiftsid">GET /api/map/shifts/:id<a class="headerlink" href="#get-apimapshiftsid" title="Permanent link">¶</a></h3>
|
||
<p>Get single shift with signups list.</p>
|
||
<p><strong>Path Parameters:</strong></p>
|
||
<ul>
|
||
<li><code>id</code> (string): Shift ID</li>
|
||
</ul>
|
||
<p><strong>Example Request:</strong></p>
|
||
<div class="language-bash highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a>curl<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Authorization: Bearer <token>"</span><span class="w"> </span><span class="se">\</span>
|
||
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a><span class="w"> </span><span class="s2">"http://api.cmlite.org/api/map/shifts/clx1234567890"</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (200 OK):</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="p">{</span>
|
||
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-6-3"><a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a><span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-6-4"><a id="__codelineno-6-4" name="__codelineno-6-4" href="#__codelineno-6-4"></a><span class="w"> </span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Canvassing residential areas in Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-6-5"><a id="__codelineno-6-5" name="__codelineno-6-5" href="#__codelineno-6-5"></a><span class="w"> </span><span class="nt">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-15T00:00:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-6-6"><a id="__codelineno-6-6" name="__codelineno-6-6" href="#__codelineno-6-6"></a><span class="w"> </span><span class="nt">"startTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10:00"</span><span class="p">,</span>
|
||
</span><span id="__span-6-7"><a id="__codelineno-6-7" name="__codelineno-6-7" href="#__codelineno-6-7"></a><span class="w"> </span><span class="nt">"endTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"14:00"</span><span class="p">,</span>
|
||
</span><span id="__span-6-8"><a id="__codelineno-6-8" name="__codelineno-6-8" href="#__codelineno-6-8"></a><span class="w"> </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123 Main St"</span><span class="p">,</span>
|
||
</span><span id="__span-6-9"><a id="__codelineno-6-9" name="__codelineno-6-9" href="#__codelineno-6-9"></a><span class="w"> </span><span class="nt">"maxVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="p">,</span>
|
||
</span><span id="__span-6-10"><a id="__codelineno-6-10" name="__codelineno-6-10" href="#__codelineno-6-10"></a><span class="w"> </span><span class="nt">"currentVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span>
|
||
</span><span id="__span-6-11"><a id="__codelineno-6-11" name="__codelineno-6-11" href="#__codelineno-6-11"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OPEN"</span><span class="p">,</span>
|
||
</span><span id="__span-6-12"><a id="__codelineno-6-12" name="__codelineno-6-12" href="#__codelineno-6-12"></a><span class="w"> </span><span class="nt">"isPublic"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
</span><span id="__span-6-13"><a id="__codelineno-6-13" name="__codelineno-6-13" href="#__codelineno-6-13"></a><span class="w"> </span><span class="nt">"cutId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx0987654321"</span><span class="p">,</span>
|
||
</span><span id="__span-6-14"><a id="__codelineno-6-14" name="__codelineno-6-14" href="#__codelineno-6-14"></a><span class="w"> </span><span class="nt">"cut"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-6-15"><a id="__codelineno-6-15" name="__codelineno-6-15" href="#__codelineno-6-15"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx0987654321"</span><span class="p">,</span>
|
||
</span><span id="__span-6-16"><a id="__codelineno-6-16" name="__codelineno-6-16" href="#__codelineno-6-16"></a><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ward 5 Residential"</span>
|
||
</span><span id="__span-6-17"><a id="__codelineno-6-17" name="__codelineno-6-17" href="#__codelineno-6-17"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-6-18"><a id="__codelineno-6-18" name="__codelineno-6-18" href="#__codelineno-6-18"></a><span class="w"> </span><span class="nt">"signups"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
</span><span id="__span-6-19"><a id="__codelineno-6-19" name="__codelineno-6-19" href="#__codelineno-6-19"></a><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-6-20"><a id="__codelineno-6-20" name="__codelineno-6-20" href="#__codelineno-6-20"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx2222222222"</span><span class="p">,</span>
|
||
</span><span id="__span-6-21"><a id="__codelineno-6-21" name="__codelineno-6-21" href="#__codelineno-6-21"></a><span class="w"> </span><span class="nt">"shiftId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-6-22"><a id="__codelineno-6-22" name="__codelineno-6-22" href="#__codelineno-6-22"></a><span class="w"> </span><span class="nt">"shiftTitle"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-6-23"><a id="__codelineno-6-23" name="__codelineno-6-23" href="#__codelineno-6-23"></a><span class="w"> </span><span class="nt">"userId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx3333333333"</span><span class="p">,</span>
|
||
</span><span id="__span-6-24"><a id="__codelineno-6-24" name="__codelineno-6-24" href="#__codelineno-6-24"></a><span class="w"> </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-6-25"><a id="__codelineno-6-25" name="__codelineno-6-25" href="#__codelineno-6-25"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx3333333333"</span><span class="p">,</span>
|
||
</span><span id="__span-6-26"><a id="__codelineno-6-26" name="__codelineno-6-26" href="#__codelineno-6-26"></a><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"volunteer@example.com"</span><span class="p">,</span>
|
||
</span><span id="__span-6-27"><a id="__codelineno-6-27" name="__codelineno-6-27" href="#__codelineno-6-27"></a><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jane Volunteer"</span><span class="p">,</span>
|
||
</span><span id="__span-6-28"><a id="__codelineno-6-28" name="__codelineno-6-28" href="#__codelineno-6-28"></a><span class="w"> </span><span class="nt">"phone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"+1234567890"</span>
|
||
</span><span id="__span-6-29"><a id="__codelineno-6-29" name="__codelineno-6-29" href="#__codelineno-6-29"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-6-30"><a id="__codelineno-6-30" name="__codelineno-6-30" href="#__codelineno-6-30"></a><span class="w"> </span><span class="nt">"userEmail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"volunteer@example.com"</span><span class="p">,</span>
|
||
</span><span id="__span-6-31"><a id="__codelineno-6-31" name="__codelineno-6-31" href="#__codelineno-6-31"></a><span class="w"> </span><span class="nt">"userName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jane Volunteer"</span><span class="p">,</span>
|
||
</span><span id="__span-6-32"><a id="__codelineno-6-32" name="__codelineno-6-32" href="#__codelineno-6-32"></a><span class="w"> </span><span class="nt">"userPhone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"+1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-6-33"><a id="__codelineno-6-33" name="__codelineno-6-33" href="#__codelineno-6-33"></a><span class="w"> </span><span class="nt">"signupDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-05T10:30:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-6-34"><a id="__codelineno-6-34" name="__codelineno-6-34" href="#__codelineno-6-34"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CONFIRMED"</span><span class="p">,</span>
|
||
</span><span id="__span-6-35"><a id="__codelineno-6-35" name="__codelineno-6-35" href="#__codelineno-6-35"></a><span class="w"> </span><span class="nt">"signupSource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PUBLIC"</span>
|
||
</span><span id="__span-6-36"><a id="__codelineno-6-36" name="__codelineno-6-36" href="#__codelineno-6-36"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-6-37"><a id="__codelineno-6-37" name="__codelineno-6-37" href="#__codelineno-6-37"></a><span class="w"> </span><span class="p">],</span>
|
||
</span><span id="__span-6-38"><a id="__codelineno-6-38" name="__codelineno-6-38" href="#__codelineno-6-38"></a><span class="w"> </span><span class="nt">"_count"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-6-39"><a id="__codelineno-6-39" name="__codelineno-6-39" href="#__codelineno-6-39"></a><span class="w"> </span><span class="nt">"signups"</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span>
|
||
</span><span id="__span-6-40"><a id="__codelineno-6-40" name="__codelineno-6-40" href="#__codelineno-6-40"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-6-41"><a id="__codelineno-6-41" name="__codelineno-6-41" href="#__codelineno-6-41"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Error Responses:</strong></p>
|
||
<ul>
|
||
<li><code>404 Not Found</code>: Shift not found</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="post-apimapshifts">POST /api/map/shifts<a class="headerlink" href="#post-apimapshifts" title="Permanent link">¶</a></h3>
|
||
<p>Create new shift.</p>
|
||
<p><strong>Request Body:</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="p">{</span>
|
||
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a><span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a><span class="w"> </span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Canvassing residential areas in Ward 5. Meet at campaign office."</span><span class="p">,</span>
|
||
</span><span id="__span-7-4"><a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a><span class="w"> </span><span class="nt">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-15"</span><span class="p">,</span>
|
||
</span><span id="__span-7-5"><a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></a><span class="w"> </span><span class="nt">"startTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10:00"</span><span class="p">,</span>
|
||
</span><span id="__span-7-6"><a id="__codelineno-7-6" name="__codelineno-7-6" href="#__codelineno-7-6"></a><span class="w"> </span><span class="nt">"endTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"14:00"</span><span class="p">,</span>
|
||
</span><span id="__span-7-7"><a id="__codelineno-7-7" name="__codelineno-7-7" href="#__codelineno-7-7"></a><span class="w"> </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123 Main St (Campaign Office)"</span><span class="p">,</span>
|
||
</span><span id="__span-7-8"><a id="__codelineno-7-8" name="__codelineno-7-8" href="#__codelineno-7-8"></a><span class="w"> </span><span class="nt">"maxVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="p">,</span>
|
||
</span><span id="__span-7-9"><a id="__codelineno-7-9" name="__codelineno-7-9" href="#__codelineno-7-9"></a><span class="w"> </span><span class="nt">"isPublic"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
</span><span id="__span-7-10"><a id="__codelineno-7-10" name="__codelineno-7-10" href="#__codelineno-7-10"></a><span class="w"> </span><span class="nt">"cutId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx0987654321"</span>
|
||
</span><span id="__span-7-11"><a id="__codelineno-7-11" name="__codelineno-7-11" href="#__codelineno-7-11"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (201 Created):</strong></p>
|
||
<p>Returns created shift object (same format as GET).</p>
|
||
<p><strong>Validation:</strong></p>
|
||
<ul>
|
||
<li><code>date</code> must be <code>YYYY-MM-DD</code> format</li>
|
||
<li><code>startTime</code>, <code>endTime</code> must be <code>HH:MM</code> format</li>
|
||
<li><code>maxVolunteers</code> must be >= 1</li>
|
||
<li><code>cutId</code> is optional (for non-canvassing shifts)</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="put-apimapshiftsid">PUT /api/map/shifts/:id<a class="headerlink" href="#put-apimapshiftsid" title="Permanent link">¶</a></h3>
|
||
<p>Update shift. Auto-updates status if capacity changes.</p>
|
||
<p><strong>Request Body (Partial):</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="p">{</span>
|
||
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a><span class="w"> </span><span class="nt">"maxVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="p">,</span>
|
||
</span><span id="__span-8-3"><a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OPEN"</span>
|
||
</span><span id="__span-8-4"><a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (200 OK):</strong></p>
|
||
<p>Returns updated shift object.</p>
|
||
<p><strong>Auto-Status Logic:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="c1">// When maxVolunteers is updated:</span>
|
||
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">currentVolunteers</span><span class="w"> </span><span class="o">>=</span><span class="w"> </span><span class="nx">newMaxVolunteers</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nx">status</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="nx">OPEN</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a><span class="w"> </span><span class="nx">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">FULL</span><span class="p">;</span>
|
||
</span><span id="__span-9-4"><a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">currentVolunteers</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="nx">newMaxVolunteers</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nx">status</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="nx">FULL</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-9-5"><a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a><span class="w"> </span><span class="nx">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">OPEN</span><span class="p">;</span>
|
||
</span><span id="__span-9-6"><a id="__codelineno-9-6" name="__codelineno-9-6" href="#__codelineno-9-6"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="delete-apimapshiftsid">DELETE /api/map/shifts/:id<a class="headerlink" href="#delete-apimapshiftsid" title="Permanent link">¶</a></h3>
|
||
<p>Delete shift. Cascade deletes all signups.</p>
|
||
<p><strong>Response (204 No Content):</strong></p>
|
||
<p>No response body.</p>
|
||
<hr />
|
||
<h3 id="post-apimapshiftsidsignups">POST /api/map/shifts/:id/signups<a class="headerlink" href="#post-apimapshiftsidsignups" title="Permanent link">¶</a></h3>
|
||
<p>Admin add volunteer signup.</p>
|
||
<p><strong>Request Body:</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-10-1"><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="p">{</span>
|
||
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a><span class="w"> </span><span class="nt">"userEmail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"volunteer@example.com"</span><span class="p">,</span>
|
||
</span><span id="__span-10-3"><a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a><span class="w"> </span><span class="nt">"userName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jane Volunteer"</span>
|
||
</span><span id="__span-10-4"><a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (201 Created):</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="p">{</span>
|
||
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx2222222222"</span><span class="p">,</span>
|
||
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a><span class="w"> </span><span class="nt">"shiftId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-11-4"><a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a><span class="w"> </span><span class="nt">"shiftTitle"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-11-5"><a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a><span class="w"> </span><span class="nt">"userId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx3333333333"</span><span class="p">,</span>
|
||
</span><span id="__span-11-6"><a id="__codelineno-11-6" name="__codelineno-11-6" href="#__codelineno-11-6"></a><span class="w"> </span><span class="nt">"userEmail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"volunteer@example.com"</span><span class="p">,</span>
|
||
</span><span id="__span-11-7"><a id="__codelineno-11-7" name="__codelineno-11-7" href="#__codelineno-11-7"></a><span class="w"> </span><span class="nt">"userName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jane Volunteer"</span><span class="p">,</span>
|
||
</span><span id="__span-11-8"><a id="__codelineno-11-8" name="__codelineno-11-8" href="#__codelineno-11-8"></a><span class="w"> </span><span class="nt">"userPhone"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
|
||
</span><span id="__span-11-9"><a id="__codelineno-11-9" name="__codelineno-11-9" href="#__codelineno-11-9"></a><span class="w"> </span><span class="nt">"signupDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-11T15:00:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-11-10"><a id="__codelineno-11-10" name="__codelineno-11-10" href="#__codelineno-11-10"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CONFIRMED"</span><span class="p">,</span>
|
||
</span><span id="__span-11-11"><a id="__codelineno-11-11" name="__codelineno-11-11" href="#__codelineno-11-11"></a><span class="w"> </span><span class="nt">"signupSource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ADMIN"</span>
|
||
</span><span id="__span-11-12"><a id="__codelineno-11-12" name="__codelineno-11-12" href="#__codelineno-11-12"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Behavior:</strong></p>
|
||
<ul>
|
||
<li>Looks up user by email (if exists, links via <code>userId</code>)</li>
|
||
<li>If signup was previously cancelled, re-activates it</li>
|
||
<li>Increments <code>currentVolunteers</code></li>
|
||
<li>Auto-updates shift status to <code>FULL</code> if capacity reached</li>
|
||
<li>Transaction ensures atomicity</li>
|
||
</ul>
|
||
<p><strong>Error Responses:</strong></p>
|
||
<ul>
|
||
<li><code>400 Bad Request</code>: Shift is full</li>
|
||
<li><code>404 Not Found</code>: Shift not found</li>
|
||
<li><code>409 Conflict</code>: Volunteer already signed up</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="delete-apimapshiftsidsignupssignupid">DELETE /api/map/shifts/:id/signups/:signupId<a class="headerlink" href="#delete-apimapshiftsidsignupssignupid" title="Permanent link">¶</a></h3>
|
||
<p>Admin remove volunteer signup.</p>
|
||
<p><strong>Path Parameters:</strong></p>
|
||
<ul>
|
||
<li><code>id</code> (string): Shift ID</li>
|
||
<li><code>signupId</code> (string): Signup ID</li>
|
||
</ul>
|
||
<p><strong>Response (204 No Content):</strong></p>
|
||
<p>No response body.</p>
|
||
<p><strong>Behavior:</strong></p>
|
||
<ul>
|
||
<li>Updates signup status to <code>CANCELLED</code> (does not delete record)</li>
|
||
<li>Decrements <code>currentVolunteers</code></li>
|
||
<li>Auto-updates shift status to <code>OPEN</code></li>
|
||
<li>Transaction ensures atomicity</li>
|
||
</ul>
|
||
<p><strong>Error Responses:</strong></p>
|
||
<ul>
|
||
<li><code>400 Bad Request</code>: Signup already cancelled</li>
|
||
<li><code>404 Not Found</code>: Signup not found</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="post-apimapshiftsidemail-details">POST /api/map/shifts/:id/email-details<a class="headerlink" href="#post-apimapshiftsidemail-details" title="Permanent link">¶</a></h3>
|
||
<p>Email shift details to all confirmed volunteers.</p>
|
||
<p><strong>Example Request:</strong></p>
|
||
<div class="language-bash highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a>curl<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="se">\</span>
|
||
</span><span id="__span-12-2"><a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a><span class="w"> </span>-H<span class="w"> </span><span class="s2">"Authorization: Bearer <token>"</span><span class="w"> </span><span class="se">\</span>
|
||
</span><span id="__span-12-3"><a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a><span class="w"> </span><span class="s2">"http://api.cmlite.org/api/map/shifts/clx1234567890/email-details"</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (200 OK):</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="p">{</span>
|
||
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a><span class="w"> </span><span class="nt">"sent"</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span>
|
||
</span><span id="__span-13-3"><a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a><span class="w"> </span><span class="nt">"failed"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span>
|
||
</span><span id="__span-13-4"><a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Email Template:</strong></p>
|
||
<p>Uses <code>shift-details.html</code> and <code>shift-details.txt</code> templates with variables:</p>
|
||
<ul>
|
||
<li><code>USER_NAME</code> — Volunteer name</li>
|
||
<li><code>SHIFT_TITLE</code> — Shift title</li>
|
||
<li><code>SHIFT_DATE</code> — Formatted date</li>
|
||
<li><code>SHIFT_START_TIME</code> — Start time</li>
|
||
<li><code>SHIFT_END_TIME</code> — End time</li>
|
||
<li><code>SHIFT_LOCATION</code> — Location</li>
|
||
<li><code>SHIFT_DESCRIPTION</code> — Description</li>
|
||
<li><code>CURRENT_VOLUNTEERS</code> — Current signup count</li>
|
||
<li><code>MAX_VOLUNTEERS</code> — Max capacity</li>
|
||
<li><code>SHIFT_STATUS</code> — Status</li>
|
||
<li><code>ORGANIZATION_NAME</code> — Site settings org name</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="volunteer-endpoint-details">Volunteer Endpoint Details<a class="headerlink" href="#volunteer-endpoint-details" title="Permanent link">¶</a></h2>
|
||
<h3 id="get-apimapshiftsvolunteerupcoming">GET /api/map/shifts/volunteer/upcoming<a class="headerlink" href="#get-apimapshiftsvolunteerupcoming" title="Permanent link">¶</a></h3>
|
||
<p>Get upcoming public shifts with signup status for current user.</p>
|
||
<p><strong>Example Request:</strong></p>
|
||
<div class="language-bash highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a>curl<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Authorization: Bearer <token>"</span><span class="w"> </span><span class="se">\</span>
|
||
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a><span class="w"> </span><span class="s2">"http://api.cmlite.org/api/map/shifts/volunteer/upcoming"</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (200 OK):</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-15-1"><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="p">[</span>
|
||
</span><span id="__span-15-2"><a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-15-3"><a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-15-4"><a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a><span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-15-5"><a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a><span class="w"> </span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Canvassing residential areas"</span><span class="p">,</span>
|
||
</span><span id="__span-15-6"><a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a><span class="w"> </span><span class="nt">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-15T00:00:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-15-7"><a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a><span class="w"> </span><span class="nt">"startTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10:00"</span><span class="p">,</span>
|
||
</span><span id="__span-15-8"><a id="__codelineno-15-8" name="__codelineno-15-8" href="#__codelineno-15-8"></a><span class="w"> </span><span class="nt">"endTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"14:00"</span><span class="p">,</span>
|
||
</span><span id="__span-15-9"><a id="__codelineno-15-9" name="__codelineno-15-9" href="#__codelineno-15-9"></a><span class="w"> </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123 Main St"</span><span class="p">,</span>
|
||
</span><span id="__span-15-10"><a id="__codelineno-15-10" name="__codelineno-15-10" href="#__codelineno-15-10"></a><span class="w"> </span><span class="nt">"maxVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="p">,</span>
|
||
</span><span id="__span-15-11"><a id="__codelineno-15-11" name="__codelineno-15-11" href="#__codelineno-15-11"></a><span class="w"> </span><span class="nt">"currentVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span>
|
||
</span><span id="__span-15-12"><a id="__codelineno-15-12" name="__codelineno-15-12" href="#__codelineno-15-12"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OPEN"</span><span class="p">,</span>
|
||
</span><span id="__span-15-13"><a id="__codelineno-15-13" name="__codelineno-15-13" href="#__codelineno-15-13"></a><span class="w"> </span><span class="nt">"isSignedUp"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span>
|
||
</span><span id="__span-15-14"><a id="__codelineno-15-14" name="__codelineno-15-14" href="#__codelineno-15-14"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-15-15"><a id="__codelineno-15-15" name="__codelineno-15-15" href="#__codelineno-15-15"></a><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-15-16"><a id="__codelineno-15-16" name="__codelineno-15-16" href="#__codelineno-15-16"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx9876543210"</span><span class="p">,</span>
|
||
</span><span id="__span-15-17"><a id="__codelineno-15-17" name="__codelineno-15-17" href="#__codelineno-15-17"></a><span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Phone Banking"</span><span class="p">,</span>
|
||
</span><span id="__span-15-18"><a id="__codelineno-15-18" name="__codelineno-15-18" href="#__codelineno-15-18"></a><span class="w"> </span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Call voters for GOTV"</span><span class="p">,</span>
|
||
</span><span id="__span-15-19"><a id="__codelineno-15-19" name="__codelineno-15-19" href="#__codelineno-15-19"></a><span class="w"> </span><span class="nt">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-16T00:00:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-15-20"><a id="__codelineno-15-20" name="__codelineno-15-20" href="#__codelineno-15-20"></a><span class="w"> </span><span class="nt">"startTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"18:00"</span><span class="p">,</span>
|
||
</span><span id="__span-15-21"><a id="__codelineno-15-21" name="__codelineno-15-21" href="#__codelineno-15-21"></a><span class="w"> </span><span class="nt">"endTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"20:00"</span><span class="p">,</span>
|
||
</span><span id="__span-15-22"><a id="__codelineno-15-22" name="__codelineno-15-22" href="#__codelineno-15-22"></a><span class="w"> </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Virtual (Zoom)"</span><span class="p">,</span>
|
||
</span><span id="__span-15-23"><a id="__codelineno-15-23" name="__codelineno-15-23" href="#__codelineno-15-23"></a><span class="w"> </span><span class="nt">"maxVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">25</span><span class="p">,</span>
|
||
</span><span id="__span-15-24"><a id="__codelineno-15-24" name="__codelineno-15-24" href="#__codelineno-15-24"></a><span class="w"> </span><span class="nt">"currentVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="p">,</span>
|
||
</span><span id="__span-15-25"><a id="__codelineno-15-25" name="__codelineno-15-25" href="#__codelineno-15-25"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OPEN"</span><span class="p">,</span>
|
||
</span><span id="__span-15-26"><a id="__codelineno-15-26" name="__codelineno-15-26" href="#__codelineno-15-26"></a><span class="w"> </span><span class="nt">"isSignedUp"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span>
|
||
</span><span id="__span-15-27"><a id="__codelineno-15-27" name="__codelineno-15-27" href="#__codelineno-15-27"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-15-28"><a id="__codelineno-15-28" name="__codelineno-15-28" href="#__codelineno-15-28"></a><span class="p">]</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Filtering:</strong></p>
|
||
<ul>
|
||
<li>Only public shifts (<code>isPublic: true</code>)</li>
|
||
<li>Only non-cancelled shifts</li>
|
||
<li>Only shifts with date >= today</li>
|
||
<li>Sorted by date ASC, then startTime ASC</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="get-apimapshiftsvolunteermy-signups">GET /api/map/shifts/volunteer/my-signups<a class="headerlink" href="#get-apimapshiftsvolunteermy-signups" title="Permanent link">¶</a></h3>
|
||
<p>Get current user's confirmed signups for upcoming shifts.</p>
|
||
<p><strong>Example Request:</strong></p>
|
||
<div class="language-bash highlight"><pre><span></span><code><span id="__span-16-1"><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a>curl<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Authorization: Bearer <token>"</span><span class="w"> </span><span class="se">\</span>
|
||
</span><span id="__span-16-2"><a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a><span class="w"> </span><span class="s2">"http://api.cmlite.org/api/map/shifts/volunteer/my-signups"</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (200 OK):</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-17-1"><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a><span class="p">[</span>
|
||
</span><span id="__span-17-2"><a id="__codelineno-17-2" name="__codelineno-17-2" href="#__codelineno-17-2"></a><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-17-3"><a id="__codelineno-17-3" name="__codelineno-17-3" href="#__codelineno-17-3"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx2222222222"</span><span class="p">,</span>
|
||
</span><span id="__span-17-4"><a id="__codelineno-17-4" name="__codelineno-17-4" href="#__codelineno-17-4"></a><span class="w"> </span><span class="nt">"shiftId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-17-5"><a id="__codelineno-17-5" name="__codelineno-17-5" href="#__codelineno-17-5"></a><span class="w"> </span><span class="nt">"shiftTitle"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-17-6"><a id="__codelineno-17-6" name="__codelineno-17-6" href="#__codelineno-17-6"></a><span class="w"> </span><span class="nt">"userId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx3333333333"</span><span class="p">,</span>
|
||
</span><span id="__span-17-7"><a id="__codelineno-17-7" name="__codelineno-17-7" href="#__codelineno-17-7"></a><span class="w"> </span><span class="nt">"userEmail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"volunteer@example.com"</span><span class="p">,</span>
|
||
</span><span id="__span-17-8"><a id="__codelineno-17-8" name="__codelineno-17-8" href="#__codelineno-17-8"></a><span class="w"> </span><span class="nt">"userName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jane Volunteer"</span><span class="p">,</span>
|
||
</span><span id="__span-17-9"><a id="__codelineno-17-9" name="__codelineno-17-9" href="#__codelineno-17-9"></a><span class="w"> </span><span class="nt">"userPhone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"+1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-17-10"><a id="__codelineno-17-10" name="__codelineno-17-10" href="#__codelineno-17-10"></a><span class="w"> </span><span class="nt">"signupDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-05T10:30:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-17-11"><a id="__codelineno-17-11" name="__codelineno-17-11" href="#__codelineno-17-11"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CONFIRMED"</span><span class="p">,</span>
|
||
</span><span id="__span-17-12"><a id="__codelineno-17-12" name="__codelineno-17-12" href="#__codelineno-17-12"></a><span class="w"> </span><span class="nt">"signupSource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PUBLIC"</span><span class="p">,</span>
|
||
</span><span id="__span-17-13"><a id="__codelineno-17-13" name="__codelineno-17-13" href="#__codelineno-17-13"></a><span class="w"> </span><span class="nt">"shift"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-17-14"><a id="__codelineno-17-14" name="__codelineno-17-14" href="#__codelineno-17-14"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-17-15"><a id="__codelineno-17-15" name="__codelineno-17-15" href="#__codelineno-17-15"></a><span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-17-16"><a id="__codelineno-17-16" name="__codelineno-17-16" href="#__codelineno-17-16"></a><span class="w"> </span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Canvassing residential areas"</span><span class="p">,</span>
|
||
</span><span id="__span-17-17"><a id="__codelineno-17-17" name="__codelineno-17-17" href="#__codelineno-17-17"></a><span class="w"> </span><span class="nt">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-15T00:00:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-17-18"><a id="__codelineno-17-18" name="__codelineno-17-18" href="#__codelineno-17-18"></a><span class="w"> </span><span class="nt">"startTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10:00"</span><span class="p">,</span>
|
||
</span><span id="__span-17-19"><a id="__codelineno-17-19" name="__codelineno-17-19" href="#__codelineno-17-19"></a><span class="w"> </span><span class="nt">"endTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"14:00"</span><span class="p">,</span>
|
||
</span><span id="__span-17-20"><a id="__codelineno-17-20" name="__codelineno-17-20" href="#__codelineno-17-20"></a><span class="w"> </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123 Main St"</span><span class="p">,</span>
|
||
</span><span id="__span-17-21"><a id="__codelineno-17-21" name="__codelineno-17-21" href="#__codelineno-17-21"></a><span class="w"> </span><span class="nt">"maxVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="p">,</span>
|
||
</span><span id="__span-17-22"><a id="__codelineno-17-22" name="__codelineno-17-22" href="#__codelineno-17-22"></a><span class="w"> </span><span class="nt">"currentVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span>
|
||
</span><span id="__span-17-23"><a id="__codelineno-17-23" name="__codelineno-17-23" href="#__codelineno-17-23"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OPEN"</span>
|
||
</span><span id="__span-17-24"><a id="__codelineno-17-24" name="__codelineno-17-24" href="#__codelineno-17-24"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-17-25"><a id="__codelineno-17-25" name="__codelineno-17-25" href="#__codelineno-17-25"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-17-26"><a id="__codelineno-17-26" name="__codelineno-17-26" href="#__codelineno-17-26"></a><span class="p">]</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Filtering:</strong></p>
|
||
<ul>
|
||
<li>Signups by current user's email</li>
|
||
<li>Only confirmed signups</li>
|
||
<li>Only shifts with date >= today</li>
|
||
<li>Only non-cancelled shifts</li>
|
||
<li>Sorted by shift date ASC</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="post-apimapshiftsvolunteeridsignup">POST /api/map/shifts/volunteer/:id/signup<a class="headerlink" href="#post-apimapshiftsvolunteeridsignup" title="Permanent link">¶</a></h3>
|
||
<p>Authenticated user signs up for shift.</p>
|
||
<p><strong>Path Parameters:</strong></p>
|
||
<ul>
|
||
<li><code>id</code> (string): Shift ID</li>
|
||
</ul>
|
||
<p><strong>Rate Limiting:</strong></p>
|
||
<p>5 requests/min per IP (<code>shiftSignupRateLimit</code> middleware)</p>
|
||
<p><strong>Example Request:</strong></p>
|
||
<div class="language-bash highlight"><pre><span></span><code><span id="__span-18-1"><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a>curl<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="se">\</span>
|
||
</span><span id="__span-18-2"><a id="__codelineno-18-2" name="__codelineno-18-2" href="#__codelineno-18-2"></a><span class="w"> </span>-H<span class="w"> </span><span class="s2">"Authorization: Bearer <token>"</span><span class="w"> </span><span class="se">\</span>
|
||
</span><span id="__span-18-3"><a id="__codelineno-18-3" name="__codelineno-18-3" href="#__codelineno-18-3"></a><span class="w"> </span><span class="s2">"http://api.cmlite.org/api/map/shifts/volunteer/clx1234567890/signup"</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (201 Created):</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-19-1"><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a><span class="p">{</span>
|
||
</span><span id="__span-19-2"><a id="__codelineno-19-2" name="__codelineno-19-2" href="#__codelineno-19-2"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx2222222222"</span><span class="p">,</span>
|
||
</span><span id="__span-19-3"><a id="__codelineno-19-3" name="__codelineno-19-3" href="#__codelineno-19-3"></a><span class="w"> </span><span class="nt">"shiftId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-19-4"><a id="__codelineno-19-4" name="__codelineno-19-4" href="#__codelineno-19-4"></a><span class="w"> </span><span class="nt">"shiftTitle"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-19-5"><a id="__codelineno-19-5" name="__codelineno-19-5" href="#__codelineno-19-5"></a><span class="w"> </span><span class="nt">"userId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx3333333333"</span><span class="p">,</span>
|
||
</span><span id="__span-19-6"><a id="__codelineno-19-6" name="__codelineno-19-6" href="#__codelineno-19-6"></a><span class="w"> </span><span class="nt">"userEmail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"volunteer@example.com"</span><span class="p">,</span>
|
||
</span><span id="__span-19-7"><a id="__codelineno-19-7" name="__codelineno-19-7" href="#__codelineno-19-7"></a><span class="w"> </span><span class="nt">"userName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jane Volunteer"</span><span class="p">,</span>
|
||
</span><span id="__span-19-8"><a id="__codelineno-19-8" name="__codelineno-19-8" href="#__codelineno-19-8"></a><span class="w"> </span><span class="nt">"userPhone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"+1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-19-9"><a id="__codelineno-19-9" name="__codelineno-19-9" href="#__codelineno-19-9"></a><span class="w"> </span><span class="nt">"signupDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-11T15:00:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-19-10"><a id="__codelineno-19-10" name="__codelineno-19-10" href="#__codelineno-19-10"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CONFIRMED"</span><span class="p">,</span>
|
||
</span><span id="__span-19-11"><a id="__codelineno-19-11" name="__codelineno-19-11" href="#__codelineno-19-11"></a><span class="w"> </span><span class="nt">"signupSource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AUTHENTICATED"</span>
|
||
</span><span id="__span-19-12"><a id="__codelineno-19-12" name="__codelineno-19-12" href="#__codelineno-19-12"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Validation:</strong></p>
|
||
<ul>
|
||
<li>Shift must be public (<code>isPublic: true</code>)</li>
|
||
<li>Shift must not be cancelled</li>
|
||
<li>Shift must not have passed (date >= today)</li>
|
||
<li>Shift must not be full</li>
|
||
<li>User must not already be signed up</li>
|
||
</ul>
|
||
<p><strong>Behavior:</strong></p>
|
||
<ul>
|
||
<li>If previously cancelled signup exists, re-activates it</li>
|
||
<li>Sends confirmation email (no temp password)</li>
|
||
<li>Increments <code>currentVolunteers</code></li>
|
||
<li>Auto-updates shift status to <code>FULL</code> if capacity reached</li>
|
||
<li>Records Prometheus metric <code>cm_shift_signups_total</code></li>
|
||
</ul>
|
||
<p><strong>Error Responses:</strong></p>
|
||
<ul>
|
||
<li><code>400 Bad Request</code>: Shift is full, cancelled, or past</li>
|
||
<li><code>403 Forbidden</code>: Shift is not public</li>
|
||
<li><code>404 Not Found</code>: Shift not found</li>
|
||
<li><code>409 Conflict</code>: Already signed up</li>
|
||
<li><code>429 Too Many Requests</code>: Rate limit exceeded</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="delete-apimapshiftsvolunteeridsignup">DELETE /api/map/shifts/volunteer/:id/signup<a class="headerlink" href="#delete-apimapshiftsvolunteeridsignup" title="Permanent link">¶</a></h3>
|
||
<p>Cancel own signup.</p>
|
||
<p><strong>Path Parameters:</strong></p>
|
||
<ul>
|
||
<li><code>id</code> (string): Shift ID</li>
|
||
</ul>
|
||
<p><strong>Example Request:</strong></p>
|
||
<div class="language-bash highlight"><pre><span></span><code><span id="__span-20-1"><a id="__codelineno-20-1" name="__codelineno-20-1" href="#__codelineno-20-1"></a>curl<span class="w"> </span>-X<span class="w"> </span>DELETE<span class="w"> </span><span class="se">\</span>
|
||
</span><span id="__span-20-2"><a id="__codelineno-20-2" name="__codelineno-20-2" href="#__codelineno-20-2"></a><span class="w"> </span>-H<span class="w"> </span><span class="s2">"Authorization: Bearer <token>"</span><span class="w"> </span><span class="se">\</span>
|
||
</span><span id="__span-20-3"><a id="__codelineno-20-3" name="__codelineno-20-3" href="#__codelineno-20-3"></a><span class="w"> </span><span class="s2">"http://api.cmlite.org/api/map/shifts/volunteer/clx1234567890/signup"</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (204 No Content):</strong></p>
|
||
<p>No response body.</p>
|
||
<p><strong>Behavior:</strong></p>
|
||
<ul>
|
||
<li>Updates signup status to <code>CANCELLED</code></li>
|
||
<li>Decrements <code>currentVolunteers</code></li>
|
||
<li>Auto-updates shift status to <code>OPEN</code></li>
|
||
</ul>
|
||
<p><strong>Error Responses:</strong></p>
|
||
<ul>
|
||
<li><code>400 Bad Request</code>: Already cancelled</li>
|
||
<li><code>404 Not Found</code>: Signup not found</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="public-endpoint-details">Public Endpoint Details<a class="headerlink" href="#public-endpoint-details" title="Permanent link">¶</a></h2>
|
||
<h3 id="get-apimapshiftspublic">GET /api/map/shifts/public<a class="headerlink" href="#get-apimapshiftspublic" title="Permanent link">¶</a></h3>
|
||
<p>List public upcoming shifts (no auth required).</p>
|
||
<p><strong>Example Request:</strong></p>
|
||
<div class="language-bash highlight"><pre><span></span><code><span id="__span-21-1"><a id="__codelineno-21-1" name="__codelineno-21-1" href="#__codelineno-21-1"></a>curl<span class="w"> </span>http://api.cmlite.org/api/map/shifts/public
|
||
</span></code></pre></div>
|
||
<p><strong>Response (200 OK):</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-22-1"><a id="__codelineno-22-1" name="__codelineno-22-1" href="#__codelineno-22-1"></a><span class="p">[</span>
|
||
</span><span id="__span-22-2"><a id="__codelineno-22-2" name="__codelineno-22-2" href="#__codelineno-22-2"></a><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-22-3"><a id="__codelineno-22-3" name="__codelineno-22-3" href="#__codelineno-22-3"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-22-4"><a id="__codelineno-22-4" name="__codelineno-22-4" href="#__codelineno-22-4"></a><span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-22-5"><a id="__codelineno-22-5" name="__codelineno-22-5" href="#__codelineno-22-5"></a><span class="w"> </span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Canvassing residential areas in Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-22-6"><a id="__codelineno-22-6" name="__codelineno-22-6" href="#__codelineno-22-6"></a><span class="w"> </span><span class="nt">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-15T00:00:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-22-7"><a id="__codelineno-22-7" name="__codelineno-22-7" href="#__codelineno-22-7"></a><span class="w"> </span><span class="nt">"startTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10:00"</span><span class="p">,</span>
|
||
</span><span id="__span-22-8"><a id="__codelineno-22-8" name="__codelineno-22-8" href="#__codelineno-22-8"></a><span class="w"> </span><span class="nt">"endTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"14:00"</span><span class="p">,</span>
|
||
</span><span id="__span-22-9"><a id="__codelineno-22-9" name="__codelineno-22-9" href="#__codelineno-22-9"></a><span class="w"> </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123 Main St"</span><span class="p">,</span>
|
||
</span><span id="__span-22-10"><a id="__codelineno-22-10" name="__codelineno-22-10" href="#__codelineno-22-10"></a><span class="w"> </span><span class="nt">"maxVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="p">,</span>
|
||
</span><span id="__span-22-11"><a id="__codelineno-22-11" name="__codelineno-22-11" href="#__codelineno-22-11"></a><span class="w"> </span><span class="nt">"currentVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span>
|
||
</span><span id="__span-22-12"><a id="__codelineno-22-12" name="__codelineno-22-12" href="#__codelineno-22-12"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OPEN"</span>
|
||
</span><span id="__span-22-13"><a id="__codelineno-22-13" name="__codelineno-22-13" href="#__codelineno-22-13"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-22-14"><a id="__codelineno-22-14" name="__codelineno-22-14" href="#__codelineno-22-14"></a><span class="p">]</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Filtering:</strong></p>
|
||
<ul>
|
||
<li>Only public shifts (<code>isPublic: true</code>)</li>
|
||
<li>Only non-cancelled shifts</li>
|
||
<li>Only shifts with date >= today</li>
|
||
<li>Sorted by date ASC, then startTime ASC</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="post-apimapshiftspublicidsignup">POST /api/map/shifts/public/:id/signup<a class="headerlink" href="#post-apimapshiftspublicidsignup" title="Permanent link">¶</a></h3>
|
||
<p>Public signup with temporary user creation.</p>
|
||
<p><strong>Path Parameters:</strong></p>
|
||
<ul>
|
||
<li><code>id</code> (string): Shift ID</li>
|
||
</ul>
|
||
<p><strong>Rate Limiting:</strong></p>
|
||
<p>5 requests/min per IP (<code>shiftSignupRateLimit</code> middleware)</p>
|
||
<p><strong>Request Body:</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-23-1"><a id="__codelineno-23-1" name="__codelineno-23-1" href="#__codelineno-23-1"></a><span class="p">{</span>
|
||
</span><span id="__span-23-2"><a id="__codelineno-23-2" name="__codelineno-23-2" href="#__codelineno-23-2"></a><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"newvolunteer@example.com"</span><span class="p">,</span>
|
||
</span><span id="__span-23-3"><a id="__codelineno-23-3" name="__codelineno-23-3" href="#__codelineno-23-3"></a><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"John Doe"</span><span class="p">,</span>
|
||
</span><span id="__span-23-4"><a id="__codelineno-23-4" name="__codelineno-23-4" href="#__codelineno-23-4"></a><span class="w"> </span><span class="nt">"phone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"+1234567890"</span>
|
||
</span><span id="__span-23-5"><a id="__codelineno-23-5" name="__codelineno-23-5" href="#__codelineno-23-5"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Response (201 Created):</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-24-1"><a id="__codelineno-24-1" name="__codelineno-24-1" href="#__codelineno-24-1"></a><span class="p">{</span>
|
||
</span><span id="__span-24-2"><a id="__codelineno-24-2" name="__codelineno-24-2" href="#__codelineno-24-2"></a><span class="w"> </span><span class="nt">"signup"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-24-3"><a id="__codelineno-24-3" name="__codelineno-24-3" href="#__codelineno-24-3"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx2222222222"</span><span class="p">,</span>
|
||
</span><span id="__span-24-4"><a id="__codelineno-24-4" name="__codelineno-24-4" href="#__codelineno-24-4"></a><span class="w"> </span><span class="nt">"shiftId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-24-5"><a id="__codelineno-24-5" name="__codelineno-24-5" href="#__codelineno-24-5"></a><span class="w"> </span><span class="nt">"shiftTitle"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-24-6"><a id="__codelineno-24-6" name="__codelineno-24-6" href="#__codelineno-24-6"></a><span class="w"> </span><span class="nt">"userId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx4444444444"</span><span class="p">,</span>
|
||
</span><span id="__span-24-7"><a id="__codelineno-24-7" name="__codelineno-24-7" href="#__codelineno-24-7"></a><span class="w"> </span><span class="nt">"userEmail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"newvolunteer@example.com"</span><span class="p">,</span>
|
||
</span><span id="__span-24-8"><a id="__codelineno-24-8" name="__codelineno-24-8" href="#__codelineno-24-8"></a><span class="w"> </span><span class="nt">"userName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"John Doe"</span><span class="p">,</span>
|
||
</span><span id="__span-24-9"><a id="__codelineno-24-9" name="__codelineno-24-9" href="#__codelineno-24-9"></a><span class="w"> </span><span class="nt">"userPhone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"+1234567890"</span><span class="p">,</span>
|
||
</span><span id="__span-24-10"><a id="__codelineno-24-10" name="__codelineno-24-10" href="#__codelineno-24-10"></a><span class="w"> </span><span class="nt">"signupDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-11T15:00:00.000Z"</span><span class="p">,</span>
|
||
</span><span id="__span-24-11"><a id="__codelineno-24-11" name="__codelineno-24-11" href="#__codelineno-24-11"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CONFIRMED"</span><span class="p">,</span>
|
||
</span><span id="__span-24-12"><a id="__codelineno-24-12" name="__codelineno-24-12" href="#__codelineno-24-12"></a><span class="w"> </span><span class="nt">"signupSource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PUBLIC"</span>
|
||
</span><span id="__span-24-13"><a id="__codelineno-24-13" name="__codelineno-24-13" href="#__codelineno-24-13"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-24-14"><a id="__codelineno-24-14" name="__codelineno-24-14" href="#__codelineno-24-14"></a><span class="w"> </span><span class="nt">"isNewUser"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span>
|
||
</span><span id="__span-24-15"><a id="__codelineno-24-15" name="__codelineno-24-15" href="#__codelineno-24-15"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Validation:</strong></p>
|
||
<ul>
|
||
<li>Shift must be public</li>
|
||
<li>Shift must be OPEN status</li>
|
||
<li>Shift date must not have passed</li>
|
||
<li>Shift must not be full</li>
|
||
<li>Email must not already be signed up</li>
|
||
</ul>
|
||
<p><strong>Behavior — New User:</strong></p>
|
||
<p>If email does not exist in database:</p>
|
||
<ol>
|
||
<li>
|
||
<p><strong>Generate readable password:</strong>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-25-1"><a id="__codelineno-25-1" name="__codelineno-25-1" href="#__codelineno-25-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">adjectives</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s1">'Blue'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Red'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Green'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Swift'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Bright'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Bold'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Calm'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Fair'</span><span class="p">];</span>
|
||
</span><span id="__span-25-2"><a id="__codelineno-25-2" name="__codelineno-25-2" href="#__codelineno-25-2"></a><span class="kd">const</span><span class="w"> </span><span class="nx">nouns</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s1">'Eagle'</span><span class="p">,</span><span class="w"> </span><span class="s1">'River'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Mountain'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Star'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Forest'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Lake'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Wolf'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Hawk'</span><span class="p">];</span>
|
||
</span><span id="__span-25-3"><a id="__codelineno-25-3" name="__codelineno-25-3" href="#__codelineno-25-3"></a>
|
||
</span><span id="__span-25-4"><a id="__codelineno-25-4" name="__codelineno-25-4" href="#__codelineno-25-4"></a><span class="kd">function</span><span class="w"> </span><span class="nx">generateReadablePassword</span><span class="p">()</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-25-5"><a id="__codelineno-25-5" name="__codelineno-25-5" href="#__codelineno-25-5"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">adj</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">adjectives</span><span class="p">[</span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">adjectives</span><span class="p">.</span><span class="nx">length</span><span class="p">)];</span>
|
||
</span><span id="__span-25-6"><a id="__codelineno-25-6" name="__codelineno-25-6" href="#__codelineno-25-6"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">noun</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">nouns</span><span class="p">[</span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">nouns</span><span class="p">.</span><span class="nx">length</span><span class="p">)];</span>
|
||
</span><span id="__span-25-7"><a id="__codelineno-25-7" name="__codelineno-25-7" href="#__codelineno-25-7"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">num</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">90</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mf">10</span><span class="p">;</span>
|
||
</span><span id="__span-25-8"><a id="__codelineno-25-8" name="__codelineno-25-8" href="#__codelineno-25-8"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="sb">`</span><span class="si">${</span><span class="nx">adj</span><span class="si">}${</span><span class="nx">noun</span><span class="si">}${</span><span class="nx">num</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span><span class="w"> </span><span class="c1">// e.g., "BlueEagle42"</span>
|
||
</span><span id="__span-25-9"><a id="__codelineno-25-9" name="__codelineno-25-9" href="#__codelineno-25-9"></a><span class="p">}</span>
|
||
</span></code></pre></div></p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Create TEMP user:</strong>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-26-1"><a id="__codelineno-26-1" name="__codelineno-26-1" href="#__codelineno-26-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">hashedPassword</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">bcrypt</span><span class="p">.</span><span class="nx">hash</span><span class="p">(</span><span class="nx">tempPassword</span><span class="p">,</span><span class="w"> </span><span class="mf">12</span><span class="p">);</span>
|
||
</span><span id="__span-26-2"><a id="__codelineno-26-2" name="__codelineno-26-2" href="#__codelineno-26-2"></a><span class="kd">const</span><span class="w"> </span><span class="nx">shiftDate</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nb">Date</span><span class="p">(</span><span class="nx">shift</span><span class="p">.</span><span class="nx">date</span><span class="p">);</span>
|
||
</span><span id="__span-26-3"><a id="__codelineno-26-3" name="__codelineno-26-3" href="#__codelineno-26-3"></a><span class="nx">shiftDate</span><span class="p">.</span><span class="nx">setDate</span><span class="p">(</span><span class="nx">shiftDate</span><span class="p">.</span><span class="nx">getDate</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mf">1</span><span class="p">);</span><span class="w"> </span><span class="c1">// Expires day after shift</span>
|
||
</span><span id="__span-26-4"><a id="__codelineno-26-4" name="__codelineno-26-4" href="#__codelineno-26-4"></a>
|
||
</span><span id="__span-26-5"><a id="__codelineno-26-5" name="__codelineno-26-5" href="#__codelineno-26-5"></a><span class="kd">const</span><span class="w"> </span><span class="nx">user</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
|
||
</span><span id="__span-26-6"><a id="__codelineno-26-6" name="__codelineno-26-6" href="#__codelineno-26-6"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-26-7"><a id="__codelineno-26-7" name="__codelineno-26-7" href="#__codelineno-26-7"></a><span class="w"> </span><span class="nx">email</span><span class="o">:</span><span class="w"> </span><span class="kt">data.email</span><span class="p">,</span>
|
||
</span><span id="__span-26-8"><a id="__codelineno-26-8" name="__codelineno-26-8" href="#__codelineno-26-8"></a><span class="w"> </span><span class="nx">password</span><span class="o">:</span><span class="w"> </span><span class="kt">hashedPassword</span><span class="p">,</span>
|
||
</span><span id="__span-26-9"><a id="__codelineno-26-9" name="__codelineno-26-9" href="#__codelineno-26-9"></a><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="kt">data.name</span><span class="p">,</span>
|
||
</span><span id="__span-26-10"><a id="__codelineno-26-10" name="__codelineno-26-10" href="#__codelineno-26-10"></a><span class="w"> </span><span class="nx">phone</span><span class="o">:</span><span class="w"> </span><span class="kt">data.phone</span><span class="p">,</span>
|
||
</span><span id="__span-26-11"><a id="__codelineno-26-11" name="__codelineno-26-11" href="#__codelineno-26-11"></a><span class="w"> </span><span class="nx">role</span><span class="o">:</span><span class="w"> </span><span class="s1">'TEMP'</span><span class="p">,</span>
|
||
</span><span id="__span-26-12"><a id="__codelineno-26-12" name="__codelineno-26-12" href="#__codelineno-26-12"></a><span class="w"> </span><span class="nx">createdVia</span><span class="o">:</span><span class="w"> </span><span class="s1">'PUBLIC_SHIFT_SIGNUP'</span><span class="p">,</span>
|
||
</span><span id="__span-26-13"><a id="__codelineno-26-13" name="__codelineno-26-13" href="#__codelineno-26-13"></a><span class="w"> </span><span class="nx">expiresAt</span><span class="o">:</span><span class="w"> </span><span class="kt">shiftDate</span><span class="p">,</span>
|
||
</span><span id="__span-26-14"><a id="__codelineno-26-14" name="__codelineno-26-14" href="#__codelineno-26-14"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-26-15"><a id="__codelineno-26-15" name="__codelineno-26-15" href="#__codelineno-26-15"></a><span class="p">});</span>
|
||
</span></code></pre></div></p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Send confirmation email with temp password:</strong>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-27-1"><a id="__codelineno-27-1" name="__codelineno-27-1" href="#__codelineno-27-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">vars</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-27-2"><a id="__codelineno-27-2" name="__codelineno-27-2" href="#__codelineno-27-2"></a><span class="w"> </span><span class="nx">USER_NAME</span><span class="o">:</span><span class="w"> </span><span class="kt">data.name</span><span class="p">,</span>
|
||
</span><span id="__span-27-3"><a id="__codelineno-27-3" name="__codelineno-27-3" href="#__codelineno-27-3"></a><span class="w"> </span><span class="nx">USER_EMAIL</span><span class="o">:</span><span class="w"> </span><span class="kt">data.email</span><span class="p">,</span>
|
||
</span><span id="__span-27-4"><a id="__codelineno-27-4" name="__codelineno-27-4" href="#__codelineno-27-4"></a><span class="w"> </span><span class="nx">SHIFT_TITLE</span><span class="o">:</span><span class="w"> </span><span class="kt">shift.title</span><span class="p">,</span>
|
||
</span><span id="__span-27-5"><a id="__codelineno-27-5" name="__codelineno-27-5" href="#__codelineno-27-5"></a><span class="w"> </span><span class="nx">SHIFT_DATE</span><span class="o">:</span><span class="w"> </span><span class="s1">'...'</span><span class="p">,</span>
|
||
</span><span id="__span-27-6"><a id="__codelineno-27-6" name="__codelineno-27-6" href="#__codelineno-27-6"></a><span class="w"> </span><span class="nx">SHIFT_TIME</span><span class="o">:</span><span class="w"> </span><span class="s1">'...'</span><span class="p">,</span>
|
||
</span><span id="__span-27-7"><a id="__codelineno-27-7" name="__codelineno-27-7" href="#__codelineno-27-7"></a><span class="w"> </span><span class="nx">SHIFT_LOCATION</span><span class="o">:</span><span class="w"> </span><span class="kt">shift.location</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">'TBD'</span><span class="p">,</span>
|
||
</span><span id="__span-27-8"><a id="__codelineno-27-8" name="__codelineno-27-8" href="#__codelineno-27-8"></a><span class="w"> </span><span class="nx">IS_NEW_USER</span><span class="o">:</span><span class="w"> </span><span class="s1">'true'</span><span class="p">,</span>
|
||
</span><span id="__span-27-9"><a id="__codelineno-27-9" name="__codelineno-27-9" href="#__codelineno-27-9"></a><span class="w"> </span><span class="nx">TEMP_PASSWORD</span><span class="o">:</span><span class="w"> </span><span class="kt">tempPassword</span><span class="p">,</span><span class="w"> </span><span class="c1">// Only included for new users</span>
|
||
</span><span id="__span-27-10"><a id="__codelineno-27-10" name="__codelineno-27-10" href="#__codelineno-27-10"></a><span class="w"> </span><span class="nx">LOGIN_URL</span><span class="o">:</span><span class="w"> </span><span class="sb">`</span><span class="si">${</span><span class="nx">siteUrl</span><span class="si">}</span><span class="sb">/login`</span><span class="p">,</span>
|
||
</span><span id="__span-27-11"><a id="__codelineno-27-11" name="__codelineno-27-11" href="#__codelineno-27-11"></a><span class="w"> </span><span class="nx">ORGANIZATION_NAME</span><span class="o">:</span><span class="w"> </span><span class="kt">orgName</span><span class="p">,</span>
|
||
</span><span id="__span-27-12"><a id="__codelineno-27-12" name="__codelineno-27-12" href="#__codelineno-27-12"></a><span class="p">};</span>
|
||
</span></code></pre></div></p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>Behavior — Existing User:</strong></p>
|
||
<p>If email exists in database:</p>
|
||
<ul>
|
||
<li>Links signup to existing user via <code>userId</code></li>
|
||
<li>No password generated or sent</li>
|
||
<li>Sets <code>signupSource</code> to <code>AUTHENTICATED</code></li>
|
||
</ul>
|
||
<p><strong>Behavior — Re-activation:</strong></p>
|
||
<p>If cancelled signup exists:</p>
|
||
<ul>
|
||
<li>Re-activates existing signup record (status → CONFIRMED)</li>
|
||
<li>Does not create duplicate signup</li>
|
||
</ul>
|
||
<p><strong>Transaction:</strong></p>
|
||
<ul>
|
||
<li>Signup creation + <code>currentVolunteers</code> increment + status update are atomic</li>
|
||
</ul>
|
||
<p><strong>Error Responses:</strong></p>
|
||
<ul>
|
||
<li><code>400 Bad Request</code>: Shift full, not open, or past</li>
|
||
<li><code>403 Forbidden</code>: Shift not public</li>
|
||
<li><code>404 Not Found</code>: Shift not found</li>
|
||
<li><code>409 Conflict</code>: Already signed up</li>
|
||
<li><code>429 Too Many Requests</code>: Rate limit exceeded</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="service-functions">Service Functions<a class="headerlink" href="#service-functions" title="Permanent link">¶</a></h2>
|
||
<h3 id="shiftsservicefindallfilters">shiftsService.findAll(filters)<a class="headerlink" href="#shiftsservicefindallfilters" title="Permanent link">¶</a></h3>
|
||
<p>List shifts with pagination, search, and filtering.</p>
|
||
<p><strong>Usage:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-28-1"><a id="__codelineno-28-1" name="__codelineno-28-1" href="#__codelineno-28-1"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">shiftsService</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'./shifts.service'</span><span class="p">;</span>
|
||
</span><span id="__span-28-2"><a id="__codelineno-28-2" name="__codelineno-28-2" href="#__codelineno-28-2"></a>
|
||
</span><span id="__span-28-3"><a id="__codelineno-28-3" name="__codelineno-28-3" href="#__codelineno-28-3"></a><span class="kd">const</span><span class="w"> </span><span class="nx">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">shiftsService</span><span class="p">.</span><span class="nx">findAll</span><span class="p">({</span>
|
||
</span><span id="__span-28-4"><a id="__codelineno-28-4" name="__codelineno-28-4" href="#__codelineno-28-4"></a><span class="w"> </span><span class="nx">page</span><span class="o">:</span><span class="w"> </span><span class="kt">1</span><span class="p">,</span>
|
||
</span><span id="__span-28-5"><a id="__codelineno-28-5" name="__codelineno-28-5" href="#__codelineno-28-5"></a><span class="w"> </span><span class="nx">limit</span><span class="o">:</span><span class="w"> </span><span class="kt">20</span><span class="p">,</span>
|
||
</span><span id="__span-28-6"><a id="__codelineno-28-6" name="__codelineno-28-6" href="#__codelineno-28-6"></a><span class="w"> </span><span class="nx">search</span><span class="o">:</span><span class="w"> </span><span class="s1">'ward 5'</span><span class="p">,</span>
|
||
</span><span id="__span-28-7"><a id="__codelineno-28-7" name="__codelineno-28-7" href="#__codelineno-28-7"></a><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="kt">ShiftStatus.OPEN</span><span class="p">,</span>
|
||
</span><span id="__span-28-8"><a id="__codelineno-28-8" name="__codelineno-28-8" href="#__codelineno-28-8"></a><span class="w"> </span><span class="nx">upcoming</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
|
||
</span><span id="__span-28-9"><a id="__codelineno-28-9" name="__codelineno-28-9" href="#__codelineno-28-9"></a><span class="w"> </span><span class="nx">sortBy</span><span class="o">:</span><span class="w"> </span><span class="s1">'date'</span><span class="p">,</span>
|
||
</span><span id="__span-28-10"><a id="__codelineno-28-10" name="__codelineno-28-10" href="#__codelineno-28-10"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="s1">'asc'</span><span class="p">,</span>
|
||
</span><span id="__span-28-11"><a id="__codelineno-28-11" name="__codelineno-28-11" href="#__codelineno-28-11"></a><span class="p">});</span>
|
||
</span><span id="__span-28-12"><a id="__codelineno-28-12" name="__codelineno-28-12" href="#__codelineno-28-12"></a>
|
||
</span><span id="__span-28-13"><a id="__codelineno-28-13" name="__codelineno-28-13" href="#__codelineno-28-13"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">shifts</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span><span class="w"> </span><span class="c1">// Array of shifts</span>
|
||
</span><span id="__span-28-14"><a id="__codelineno-28-14" name="__codelineno-28-14" href="#__codelineno-28-14"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">pagination</span><span class="p">);</span><span class="w"> </span><span class="c1">// { page, limit, total, totalPages }</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Search Behavior:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-29-1"><a id="__codelineno-29-1" name="__codelineno-29-1" href="#__codelineno-29-1"></a><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">search</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-29-2"><a id="__codelineno-29-2" name="__codelineno-29-2" href="#__codelineno-29-2"></a><span class="w"> </span><span class="nx">where</span><span class="p">.</span><span class="nx">OR</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
|
||
</span><span id="__span-29-3"><a id="__codelineno-29-3" name="__codelineno-29-3" href="#__codelineno-29-3"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">title</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">contains</span><span class="o">:</span><span class="w"> </span><span class="kt">search</span><span class="p">,</span><span class="w"> </span><span class="nx">mode</span><span class="o">:</span><span class="w"> </span><span class="s1">'insensitive'</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-29-4"><a id="__codelineno-29-4" name="__codelineno-29-4" href="#__codelineno-29-4"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">location</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">contains</span><span class="o">:</span><span class="w"> </span><span class="kt">search</span><span class="p">,</span><span class="w"> </span><span class="nx">mode</span><span class="o">:</span><span class="w"> </span><span class="s1">'insensitive'</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-29-5"><a id="__codelineno-29-5" name="__codelineno-29-5" href="#__codelineno-29-5"></a><span class="w"> </span><span class="p">];</span>
|
||
</span><span id="__span-29-6"><a id="__codelineno-29-6" name="__codelineno-29-6" href="#__codelineno-29-6"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="shiftsservicefindbyidid">shiftsService.findById(id)<a class="headerlink" href="#shiftsservicefindbyidid" title="Permanent link">¶</a></h3>
|
||
<p>Get single shift with signups list.</p>
|
||
<p><strong>Usage:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-30-1"><a id="__codelineno-30-1" name="__codelineno-30-1" href="#__codelineno-30-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">shift</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">shiftsService</span><span class="p">.</span><span class="nx">findById</span><span class="p">(</span><span class="s1">'clx1234567890'</span><span class="p">);</span>
|
||
</span><span id="__span-30-2"><a id="__codelineno-30-2" name="__codelineno-30-2" href="#__codelineno-30-2"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">shift</span><span class="p">.</span><span class="nx">signups</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span><span class="w"> </span><span class="c1">// Confirmed signups only</span>
|
||
</span><span id="__span-30-3"><a id="__codelineno-30-3" name="__codelineno-30-3" href="#__codelineno-30-3"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">shift</span><span class="p">.</span><span class="nx">cut</span><span class="o">?</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span><span class="w"> </span><span class="c1">// Cut name if associated</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Throws:</strong></p>
|
||
<ul>
|
||
<li><code>AppError(404)</code> if shift not found</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="shiftsservicecreatedata-userid">shiftsService.create(data, userId)<a class="headerlink" href="#shiftsservicecreatedata-userid" title="Permanent link">¶</a></h3>
|
||
<p>Create shift.</p>
|
||
<p><strong>Usage:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-31-1"><a id="__codelineno-31-1" name="__codelineno-31-1" href="#__codelineno-31-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">shift</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">shiftsService</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
|
||
</span><span id="__span-31-2"><a id="__codelineno-31-2" name="__codelineno-31-2" href="#__codelineno-31-2"></a><span class="w"> </span><span class="nx">title</span><span class="o">:</span><span class="w"> </span><span class="s1">'Door Knocking — Ward 5'</span><span class="p">,</span>
|
||
</span><span id="__span-31-3"><a id="__codelineno-31-3" name="__codelineno-31-3" href="#__codelineno-31-3"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">'Canvassing residential areas'</span><span class="p">,</span>
|
||
</span><span id="__span-31-4"><a id="__codelineno-31-4" name="__codelineno-31-4" href="#__codelineno-31-4"></a><span class="w"> </span><span class="nx">date</span><span class="o">:</span><span class="w"> </span><span class="s1">'2026-02-15'</span><span class="p">,</span>
|
||
</span><span id="__span-31-5"><a id="__codelineno-31-5" name="__codelineno-31-5" href="#__codelineno-31-5"></a><span class="w"> </span><span class="nx">startTime</span><span class="o">:</span><span class="w"> </span><span class="s1">'10:00'</span><span class="p">,</span>
|
||
</span><span id="__span-31-6"><a id="__codelineno-31-6" name="__codelineno-31-6" href="#__codelineno-31-6"></a><span class="w"> </span><span class="nx">endTime</span><span class="o">:</span><span class="w"> </span><span class="s1">'14:00'</span><span class="p">,</span>
|
||
</span><span id="__span-31-7"><a id="__codelineno-31-7" name="__codelineno-31-7" href="#__codelineno-31-7"></a><span class="w"> </span><span class="nx">location</span><span class="o">:</span><span class="w"> </span><span class="s1">'123 Main St'</span><span class="p">,</span>
|
||
</span><span id="__span-31-8"><a id="__codelineno-31-8" name="__codelineno-31-8" href="#__codelineno-31-8"></a><span class="w"> </span><span class="nx">maxVolunteers</span><span class="o">:</span><span class="w"> </span><span class="kt">15</span><span class="p">,</span>
|
||
</span><span id="__span-31-9"><a id="__codelineno-31-9" name="__codelineno-31-9" href="#__codelineno-31-9"></a><span class="w"> </span><span class="nx">isPublic</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
|
||
</span><span id="__span-31-10"><a id="__codelineno-31-10" name="__codelineno-31-10" href="#__codelineno-31-10"></a><span class="w"> </span><span class="nx">cutId</span><span class="o">:</span><span class="w"> </span><span class="s1">'clx0987654321'</span><span class="p">,</span>
|
||
</span><span id="__span-31-11"><a id="__codelineno-31-11" name="__codelineno-31-11" href="#__codelineno-31-11"></a><span class="p">},</span><span class="w"> </span><span class="nx">req</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">id</span><span class="p">);</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="shiftsserviceupdateid-data">shiftsService.update(id, data)<a class="headerlink" href="#shiftsserviceupdateid-data" title="Permanent link">¶</a></h3>
|
||
<p>Update shift with auto-status management.</p>
|
||
<p><strong>Usage:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-32-1"><a id="__codelineno-32-1" name="__codelineno-32-1" href="#__codelineno-32-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">shift</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">shiftsService</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="s1">'clx1234567890'</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-32-2"><a id="__codelineno-32-2" name="__codelineno-32-2" href="#__codelineno-32-2"></a><span class="w"> </span><span class="nx">maxVolunteers</span><span class="o">:</span><span class="w"> </span><span class="kt">20</span><span class="p">,</span>
|
||
</span><span id="__span-32-3"><a id="__codelineno-32-3" name="__codelineno-32-3" href="#__codelineno-32-3"></a><span class="p">});</span>
|
||
</span><span id="__span-32-4"><a id="__codelineno-32-4" name="__codelineno-32-4" href="#__codelineno-32-4"></a>
|
||
</span><span id="__span-32-5"><a id="__codelineno-32-5" name="__codelineno-32-5" href="#__codelineno-32-5"></a><span class="c1">// If currentVolunteers was 15 and maxVolunteers was 15:</span>
|
||
</span><span id="__span-32-6"><a id="__codelineno-32-6" name="__codelineno-32-6" href="#__codelineno-32-6"></a><span class="c1">// - Old status: FULL</span>
|
||
</span><span id="__span-32-7"><a id="__codelineno-32-7" name="__codelineno-32-7" href="#__codelineno-32-7"></a><span class="c1">// - New status: OPEN (because 15 < 20)</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Auto-Status Logic:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-33-1"><a id="__codelineno-33-1" name="__codelineno-33-1" href="#__codelineno-33-1"></a><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">maxVolunteers</span><span class="w"> </span><span class="o">!==</span><span class="w"> </span><span class="kc">undefined</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-33-2"><a id="__codelineno-33-2" name="__codelineno-33-2" href="#__codelineno-33-2"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">existing</span><span class="p">.</span><span class="nx">currentVolunteers</span><span class="w"> </span><span class="o">>=</span><span class="w"> </span><span class="nx">data</span><span class="p">.</span><span class="nx">maxVolunteers</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nx">existing</span><span class="p">.</span><span class="nx">status</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="nx">ShiftStatus</span><span class="p">.</span><span class="nx">OPEN</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-33-3"><a id="__codelineno-33-3" name="__codelineno-33-3" href="#__codelineno-33-3"></a><span class="w"> </span><span class="nx">updateData</span><span class="p">.</span><span class="nx">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">ShiftStatus</span><span class="p">.</span><span class="nx">FULL</span><span class="p">;</span>
|
||
</span><span id="__span-33-4"><a id="__codelineno-33-4" name="__codelineno-33-4" href="#__codelineno-33-4"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">existing</span><span class="p">.</span><span class="nx">currentVolunteers</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="nx">data</span><span class="p">.</span><span class="nx">maxVolunteers</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nx">existing</span><span class="p">.</span><span class="nx">status</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="nx">ShiftStatus</span><span class="p">.</span><span class="nx">FULL</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-33-5"><a id="__codelineno-33-5" name="__codelineno-33-5" href="#__codelineno-33-5"></a><span class="w"> </span><span class="nx">updateData</span><span class="p">.</span><span class="nx">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">ShiftStatus</span><span class="p">.</span><span class="nx">OPEN</span><span class="p">;</span>
|
||
</span><span id="__span-33-6"><a id="__codelineno-33-6" name="__codelineno-33-6" href="#__codelineno-33-6"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-33-7"><a id="__codelineno-33-7" name="__codelineno-33-7" href="#__codelineno-33-7"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="shiftsserviceaddsignupshiftid-data">shiftsService.addSignup(shiftId, data)<a class="headerlink" href="#shiftsserviceaddsignupshiftid-data" title="Permanent link">¶</a></h3>
|
||
<p>Admin add volunteer signup.</p>
|
||
<p><strong>Usage:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-34-1"><a id="__codelineno-34-1" name="__codelineno-34-1" href="#__codelineno-34-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">signup</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">shiftsService</span><span class="p">.</span><span class="nx">addSignup</span><span class="p">(</span><span class="s1">'clx1234567890'</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-34-2"><a id="__codelineno-34-2" name="__codelineno-34-2" href="#__codelineno-34-2"></a><span class="w"> </span><span class="nx">userEmail</span><span class="o">:</span><span class="w"> </span><span class="s1">'volunteer@example.com'</span><span class="p">,</span>
|
||
</span><span id="__span-34-3"><a id="__codelineno-34-3" name="__codelineno-34-3" href="#__codelineno-34-3"></a><span class="w"> </span><span class="nx">userName</span><span class="o">:</span><span class="w"> </span><span class="s1">'Jane Volunteer'</span><span class="p">,</span>
|
||
</span><span id="__span-34-4"><a id="__codelineno-34-4" name="__codelineno-34-4" href="#__codelineno-34-4"></a><span class="p">});</span>
|
||
</span><span id="__span-34-5"><a id="__codelineno-34-5" name="__codelineno-34-5" href="#__codelineno-34-5"></a>
|
||
</span><span id="__span-34-6"><a id="__codelineno-34-6" name="__codelineno-34-6" href="#__codelineno-34-6"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">signup</span><span class="p">.</span><span class="nx">signupSource</span><span class="p">);</span><span class="w"> </span><span class="c1">// 'ADMIN'</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Behavior:</strong></p>
|
||
<ul>
|
||
<li>Looks up user by email</li>
|
||
<li>Re-activates cancelled signup if exists</li>
|
||
<li>Atomic transaction (signup + capacity + status)</li>
|
||
</ul>
|
||
<p><strong>Throws:</strong></p>
|
||
<ul>
|
||
<li><code>AppError(400)</code> if shift full</li>
|
||
<li><code>AppError(404)</code> if shift not found</li>
|
||
<li><code>AppError(409)</code> if already signed up</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="shiftsservicepublicsignupshiftid-data">shiftsService.publicSignup(shiftId, data)<a class="headerlink" href="#shiftsservicepublicsignupshiftid-data" title="Permanent link">¶</a></h3>
|
||
<p>Public signup with temp user creation.</p>
|
||
<p><strong>Usage:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-35-1"><a id="__codelineno-35-1" name="__codelineno-35-1" href="#__codelineno-35-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">shiftsService</span><span class="p">.</span><span class="nx">publicSignup</span><span class="p">(</span><span class="s1">'clx1234567890'</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-35-2"><a id="__codelineno-35-2" name="__codelineno-35-2" href="#__codelineno-35-2"></a><span class="w"> </span><span class="nx">email</span><span class="o">:</span><span class="w"> </span><span class="s1">'newuser@example.com'</span><span class="p">,</span>
|
||
</span><span id="__span-35-3"><a id="__codelineno-35-3" name="__codelineno-35-3" href="#__codelineno-35-3"></a><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">'John Doe'</span><span class="p">,</span>
|
||
</span><span id="__span-35-4"><a id="__codelineno-35-4" name="__codelineno-35-4" href="#__codelineno-35-4"></a><span class="w"> </span><span class="nx">phone</span><span class="o">:</span><span class="w"> </span><span class="s1">'+1234567890'</span><span class="p">,</span>
|
||
</span><span id="__span-35-5"><a id="__codelineno-35-5" name="__codelineno-35-5" href="#__codelineno-35-5"></a><span class="p">});</span>
|
||
</span><span id="__span-35-6"><a id="__codelineno-35-6" name="__codelineno-35-6" href="#__codelineno-35-6"></a>
|
||
</span><span id="__span-35-7"><a id="__codelineno-35-7" name="__codelineno-35-7" href="#__codelineno-35-7"></a><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">isNewUser</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-35-8"><a id="__codelineno-35-8" name="__codelineno-35-8" href="#__codelineno-35-8"></a><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Created TEMP user with readable password'</span><span class="p">);</span>
|
||
</span><span id="__span-35-9"><a id="__codelineno-35-9" name="__codelineno-35-9" href="#__codelineno-35-9"></a><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Confirmation email sent with credentials'</span><span class="p">);</span>
|
||
</span><span id="__span-35-10"><a id="__codelineno-35-10" name="__codelineno-35-10" href="#__codelineno-35-10"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Temp User Expiry:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-36-1"><a id="__codelineno-36-1" name="__codelineno-36-1" href="#__codelineno-36-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">shiftDate</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nb">Date</span><span class="p">(</span><span class="nx">shift</span><span class="p">.</span><span class="nx">date</span><span class="p">);</span>
|
||
</span><span id="__span-36-2"><a id="__codelineno-36-2" name="__codelineno-36-2" href="#__codelineno-36-2"></a><span class="nx">shiftDate</span><span class="p">.</span><span class="nx">setDate</span><span class="p">(</span><span class="nx">shiftDate</span><span class="p">.</span><span class="nx">getDate</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mf">1</span><span class="p">);</span><span class="w"> </span><span class="c1">// Expires day after shift</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Email Template Variables:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-37-1"><a id="__codelineno-37-1" name="__codelineno-37-1" href="#__codelineno-37-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">vars</span><span class="o">:</span><span class="w"> </span><span class="kt">Record</span><span class="o"><</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="kt">string</span><span class="o">></span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-37-2"><a id="__codelineno-37-2" name="__codelineno-37-2" href="#__codelineno-37-2"></a><span class="w"> </span><span class="nx">USER_NAME</span><span class="o">:</span><span class="w"> </span><span class="kt">data.name</span><span class="p">,</span>
|
||
</span><span id="__span-37-3"><a id="__codelineno-37-3" name="__codelineno-37-3" href="#__codelineno-37-3"></a><span class="w"> </span><span class="nx">USER_EMAIL</span><span class="o">:</span><span class="w"> </span><span class="kt">data.email</span><span class="p">,</span>
|
||
</span><span id="__span-37-4"><a id="__codelineno-37-4" name="__codelineno-37-4" href="#__codelineno-37-4"></a><span class="w"> </span><span class="nx">SHIFT_TITLE</span><span class="o">:</span><span class="w"> </span><span class="kt">shift.title</span><span class="p">,</span>
|
||
</span><span id="__span-37-5"><a id="__codelineno-37-5" name="__codelineno-37-5" href="#__codelineno-37-5"></a><span class="w"> </span><span class="nx">SHIFT_DATE</span><span class="o">:</span><span class="w"> </span><span class="kt">dateStr</span><span class="p">,</span>
|
||
</span><span id="__span-37-6"><a id="__codelineno-37-6" name="__codelineno-37-6" href="#__codelineno-37-6"></a><span class="w"> </span><span class="nx">SHIFT_TIME</span><span class="o">:</span><span class="w"> </span><span class="sb">`</span><span class="si">${</span><span class="nx">shift</span><span class="p">.</span><span class="nx">startTime</span><span class="si">}</span><span class="sb"> — </span><span class="si">${</span><span class="nx">shift</span><span class="p">.</span><span class="nx">endTime</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span>
|
||
</span><span id="__span-37-7"><a id="__codelineno-37-7" name="__codelineno-37-7" href="#__codelineno-37-7"></a><span class="w"> </span><span class="nx">SHIFT_LOCATION</span><span class="o">:</span><span class="w"> </span><span class="kt">shift.location</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">'TBD'</span><span class="p">,</span>
|
||
</span><span id="__span-37-8"><a id="__codelineno-37-8" name="__codelineno-37-8" href="#__codelineno-37-8"></a><span class="w"> </span><span class="nx">IS_NEW_USER</span><span class="o">:</span><span class="w"> </span><span class="kt">isNewUser</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="s1">'true'</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="s1">''</span><span class="p">,</span><span class="w"> </span><span class="c1">// Conditional content in template</span>
|
||
</span><span id="__span-37-9"><a id="__codelineno-37-9" name="__codelineno-37-9" href="#__codelineno-37-9"></a><span class="w"> </span><span class="nx">TEMP_PASSWORD</span><span class="o">:</span><span class="w"> </span><span class="kt">tempPassword</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">''</span><span class="p">,</span><span class="w"> </span><span class="c1">// Only included for new users</span>
|
||
</span><span id="__span-37-10"><a id="__codelineno-37-10" name="__codelineno-37-10" href="#__codelineno-37-10"></a><span class="w"> </span><span class="nx">LOGIN_URL</span><span class="o">:</span><span class="w"> </span><span class="sb">`</span><span class="si">${</span><span class="nx">baseUrl</span><span class="si">}</span><span class="sb">/login`</span><span class="p">,</span>
|
||
</span><span id="__span-37-11"><a id="__codelineno-37-11" name="__codelineno-37-11" href="#__codelineno-37-11"></a><span class="w"> </span><span class="nx">ORGANIZATION_NAME</span><span class="o">:</span><span class="w"> </span><span class="kt">orgName</span><span class="p">,</span>
|
||
</span><span id="__span-37-12"><a id="__codelineno-37-12" name="__codelineno-37-12" href="#__codelineno-37-12"></a><span class="p">};</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Metrics:</strong></p>
|
||
<p>Records <code>cm_shift_signups_total</code> Prometheus counter.</p>
|
||
<p><strong>Throws:</strong></p>
|
||
<ul>
|
||
<li><code>AppError(400)</code> if shift full, not open, or past</li>
|
||
<li><code>AppError(403)</code> if not public</li>
|
||
<li><code>AppError(404)</code> if shift not found</li>
|
||
<li><code>AppError(409)</code> if duplicate signup</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="shiftsserviceremovesignupsignupid">shiftsService.removeSignup(signupId)<a class="headerlink" href="#shiftsserviceremovesignupsignupid" title="Permanent link">¶</a></h3>
|
||
<p>Cancel signup (admin).</p>
|
||
<p><strong>Usage:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-38-1"><a id="__codelineno-38-1" name="__codelineno-38-1" href="#__codelineno-38-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">shiftsService</span><span class="p">.</span><span class="nx">removeSignup</span><span class="p">(</span><span class="s1">'clx2222222222'</span><span class="p">);</span>
|
||
</span><span id="__span-38-2"><a id="__codelineno-38-2" name="__codelineno-38-2" href="#__codelineno-38-2"></a>
|
||
</span><span id="__span-38-3"><a id="__codelineno-38-3" name="__codelineno-38-3" href="#__codelineno-38-3"></a><span class="c1">// Signup status → CANCELLED</span>
|
||
</span><span id="__span-38-4"><a id="__codelineno-38-4" name="__codelineno-38-4" href="#__codelineno-38-4"></a><span class="c1">// currentVolunteers decremented</span>
|
||
</span><span id="__span-38-5"><a id="__codelineno-38-5" name="__codelineno-38-5" href="#__codelineno-38-5"></a><span class="c1">// Shift status → OPEN</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Atomic Transaction:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-39-1"><a id="__codelineno-39-1" name="__codelineno-39-1" href="#__codelineno-39-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">$transaction</span><span class="p">([</span>
|
||
</span><span id="__span-39-2"><a id="__codelineno-39-2" name="__codelineno-39-2" href="#__codelineno-39-2"></a><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">shiftSignup</span><span class="p">.</span><span class="nx">update</span><span class="p">({</span>
|
||
</span><span id="__span-39-3"><a id="__codelineno-39-3" name="__codelineno-39-3" href="#__codelineno-39-3"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="w"> </span><span class="kt">signupId</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-39-4"><a id="__codelineno-39-4" name="__codelineno-39-4" href="#__codelineno-39-4"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="kt">SignupStatus.CANCELLED</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-39-5"><a id="__codelineno-39-5" name="__codelineno-39-5" href="#__codelineno-39-5"></a><span class="w"> </span><span class="p">}),</span>
|
||
</span><span id="__span-39-6"><a id="__codelineno-39-6" name="__codelineno-39-6" href="#__codelineno-39-6"></a><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">shift</span><span class="p">.</span><span class="nx">update</span><span class="p">({</span>
|
||
</span><span id="__span-39-7"><a id="__codelineno-39-7" name="__codelineno-39-7" href="#__codelineno-39-7"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="w"> </span><span class="kt">signup.shiftId</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-39-8"><a id="__codelineno-39-8" name="__codelineno-39-8" href="#__codelineno-39-8"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-39-9"><a id="__codelineno-39-9" name="__codelineno-39-9" href="#__codelineno-39-9"></a><span class="w"> </span><span class="nx">currentVolunteers</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">decrement</span><span class="o">:</span><span class="w"> </span><span class="kt">1</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-39-10"><a id="__codelineno-39-10" name="__codelineno-39-10" href="#__codelineno-39-10"></a><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="kt">ShiftStatus.OPEN</span><span class="p">,</span>
|
||
</span><span id="__span-39-11"><a id="__codelineno-39-11" name="__codelineno-39-11" href="#__codelineno-39-11"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-39-12"><a id="__codelineno-39-12" name="__codelineno-39-12" href="#__codelineno-39-12"></a><span class="w"> </span><span class="p">}),</span>
|
||
</span><span id="__span-39-13"><a id="__codelineno-39-13" name="__codelineno-39-13" href="#__codelineno-39-13"></a><span class="p">]);</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="shiftsserviceemailshiftdetailsshiftid">shiftsService.emailShiftDetails(shiftId)<a class="headerlink" href="#shiftsserviceemailshiftdetailsshiftid" title="Permanent link">¶</a></h3>
|
||
<p>Email shift details to all confirmed volunteers.</p>
|
||
<p><strong>Usage:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-40-1"><a id="__codelineno-40-1" name="__codelineno-40-1" href="#__codelineno-40-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">shiftsService</span><span class="p">.</span><span class="nx">emailShiftDetails</span><span class="p">(</span><span class="s1">'clx1234567890'</span><span class="p">);</span>
|
||
</span><span id="__span-40-2"><a id="__codelineno-40-2" name="__codelineno-40-2" href="#__codelineno-40-2"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Sent: </span><span class="si">${</span><span class="nx">result</span><span class="p">.</span><span class="nx">sent</span><span class="si">}</span><span class="sb">, Failed: </span><span class="si">${</span><span class="nx">result</span><span class="p">.</span><span class="nx">failed</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Email Template:</strong></p>
|
||
<p>Uses <code>shift-details.html</code> and <code>shift-details.txt</code> with variables:</p>
|
||
<ul>
|
||
<li><code>USER_NAME</code></li>
|
||
<li><code>SHIFT_TITLE</code></li>
|
||
<li><code>SHIFT_DATE</code></li>
|
||
<li><code>SHIFT_START_TIME</code></li>
|
||
<li><code>SHIFT_END_TIME</code></li>
|
||
<li><code>SHIFT_LOCATION</code></li>
|
||
<li><code>SHIFT_DESCRIPTION</code></li>
|
||
<li><code>CURRENT_VOLUNTEERS</code></li>
|
||
<li><code>MAX_VOLUNTEERS</code></li>
|
||
<li><code>SHIFT_STATUS</code></li>
|
||
<li><code>ORGANIZATION_NAME</code></li>
|
||
</ul>
|
||
<p><strong>Error Handling:</strong></p>
|
||
<p>Individual email failures are logged but do not stop batch processing.</p>
|
||
<hr />
|
||
<h2 id="validation-schemas">Validation Schemas<a class="headerlink" href="#validation-schemas" title="Permanent link">¶</a></h2>
|
||
<h3 id="create-shift-schema">Create Shift Schema<a class="headerlink" href="#create-shift-schema" title="Permanent link">¶</a></h3>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-41-1"><a id="__codelineno-41-1" name="__codelineno-41-1" href="#__codelineno-41-1"></a><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">createShiftSchema</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">z</span><span class="p">.</span><span class="nx">object</span><span class="p">({</span>
|
||
</span><span id="__span-41-2"><a id="__codelineno-41-2" name="__codelineno-41-2" href="#__codelineno-41-2"></a><span class="w"> </span><span class="nx">title</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">min</span><span class="p">(</span><span class="mf">1</span><span class="p">,</span><span class="w"> </span><span class="s1">'Title is required'</span><span class="p">),</span>
|
||
</span><span id="__span-41-3"><a id="__codelineno-41-3" name="__codelineno-41-3" href="#__codelineno-41-3"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-41-4"><a id="__codelineno-41-4" name="__codelineno-41-4" href="#__codelineno-41-4"></a><span class="w"> </span><span class="nx">date</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">regex</span><span class="p">(</span><span class="sr">/^\d{4}-\d{2}-\d{2}$/</span><span class="p">,</span><span class="w"> </span><span class="s1">'Date must be YYYY-MM-DD'</span><span class="p">),</span>
|
||
</span><span id="__span-41-5"><a id="__codelineno-41-5" name="__codelineno-41-5" href="#__codelineno-41-5"></a><span class="w"> </span><span class="nx">startTime</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">regex</span><span class="p">(</span><span class="sr">/^\d{2}:\d{2}$/</span><span class="p">,</span><span class="w"> </span><span class="s1">'Start time must be HH:MM'</span><span class="p">),</span>
|
||
</span><span id="__span-41-6"><a id="__codelineno-41-6" name="__codelineno-41-6" href="#__codelineno-41-6"></a><span class="w"> </span><span class="nx">endTime</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">regex</span><span class="p">(</span><span class="sr">/^\d{2}:\d{2}$/</span><span class="p">,</span><span class="w"> </span><span class="s1">'End time must be HH:MM'</span><span class="p">),</span>
|
||
</span><span id="__span-41-7"><a id="__codelineno-41-7" name="__codelineno-41-7" href="#__codelineno-41-7"></a><span class="w"> </span><span class="nx">location</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-41-8"><a id="__codelineno-41-8" name="__codelineno-41-8" href="#__codelineno-41-8"></a><span class="w"> </span><span class="nx">maxVolunteers</span><span class="o">:</span><span class="w"> </span><span class="kt">z.number</span><span class="p">().</span><span class="kr">int</span><span class="p">().</span><span class="nx">min</span><span class="p">(</span><span class="mf">1</span><span class="p">,</span><span class="w"> </span><span class="s1">'Must have at least 1 volunteer spot'</span><span class="p">),</span>
|
||
</span><span id="__span-41-9"><a id="__codelineno-41-9" name="__codelineno-41-9" href="#__codelineno-41-9"></a><span class="w"> </span><span class="nx">isPublic</span><span class="o">:</span><span class="w"> </span><span class="kt">z.boolean</span><span class="p">().</span><span class="nx">optional</span><span class="p">().</span><span class="k">default</span><span class="p">(</span><span class="kc">false</span><span class="p">),</span>
|
||
</span><span id="__span-41-10"><a id="__codelineno-41-10" name="__codelineno-41-10" href="#__codelineno-41-10"></a><span class="w"> </span><span class="nx">cutId</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-41-11"><a id="__codelineno-41-11" name="__codelineno-41-11" href="#__codelineno-41-11"></a><span class="p">});</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Example Valid Input:</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-42-1"><a id="__codelineno-42-1" name="__codelineno-42-1" href="#__codelineno-42-1"></a><span class="p">{</span>
|
||
</span><span id="__span-42-2"><a id="__codelineno-42-2" name="__codelineno-42-2" href="#__codelineno-42-2"></a><span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Door Knocking — Ward 5"</span><span class="p">,</span>
|
||
</span><span id="__span-42-3"><a id="__codelineno-42-3" name="__codelineno-42-3" href="#__codelineno-42-3"></a><span class="w"> </span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Canvassing residential areas"</span><span class="p">,</span>
|
||
</span><span id="__span-42-4"><a id="__codelineno-42-4" name="__codelineno-42-4" href="#__codelineno-42-4"></a><span class="w"> </span><span class="nt">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-15"</span><span class="p">,</span>
|
||
</span><span id="__span-42-5"><a id="__codelineno-42-5" name="__codelineno-42-5" href="#__codelineno-42-5"></a><span class="w"> </span><span class="nt">"startTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10:00"</span><span class="p">,</span>
|
||
</span><span id="__span-42-6"><a id="__codelineno-42-6" name="__codelineno-42-6" href="#__codelineno-42-6"></a><span class="w"> </span><span class="nt">"endTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"14:00"</span><span class="p">,</span>
|
||
</span><span id="__span-42-7"><a id="__codelineno-42-7" name="__codelineno-42-7" href="#__codelineno-42-7"></a><span class="w"> </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123 Main St"</span><span class="p">,</span>
|
||
</span><span id="__span-42-8"><a id="__codelineno-42-8" name="__codelineno-42-8" href="#__codelineno-42-8"></a><span class="w"> </span><span class="nt">"maxVolunteers"</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="p">,</span>
|
||
</span><span id="__span-42-9"><a id="__codelineno-42-9" name="__codelineno-42-9" href="#__codelineno-42-9"></a><span class="w"> </span><span class="nt">"isPublic"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
</span><span id="__span-42-10"><a id="__codelineno-42-10" name="__codelineno-42-10" href="#__codelineno-42-10"></a><span class="w"> </span><span class="nt">"cutId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"clx0987654321"</span>
|
||
</span><span id="__span-42-11"><a id="__codelineno-42-11" name="__codelineno-42-11" href="#__codelineno-42-11"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="update-shift-schema">Update Shift Schema<a class="headerlink" href="#update-shift-schema" title="Permanent link">¶</a></h3>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-43-1"><a id="__codelineno-43-1" name="__codelineno-43-1" href="#__codelineno-43-1"></a><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">updateShiftSchema</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">z</span><span class="p">.</span><span class="nx">object</span><span class="p">({</span>
|
||
</span><span id="__span-43-2"><a id="__codelineno-43-2" name="__codelineno-43-2" href="#__codelineno-43-2"></a><span class="w"> </span><span class="nx">title</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">min</span><span class="p">(</span><span class="mf">1</span><span class="p">).</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-43-3"><a id="__codelineno-43-3" name="__codelineno-43-3" href="#__codelineno-43-3"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">nullable</span><span class="p">().</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-43-4"><a id="__codelineno-43-4" name="__codelineno-43-4" href="#__codelineno-43-4"></a><span class="w"> </span><span class="nx">date</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">regex</span><span class="p">(</span><span class="sr">/^\d{4}-\d{2}-\d{2}$/</span><span class="p">).</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-43-5"><a id="__codelineno-43-5" name="__codelineno-43-5" href="#__codelineno-43-5"></a><span class="w"> </span><span class="nx">startTime</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">regex</span><span class="p">(</span><span class="sr">/^\d{2}:\d{2}$/</span><span class="p">).</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-43-6"><a id="__codelineno-43-6" name="__codelineno-43-6" href="#__codelineno-43-6"></a><span class="w"> </span><span class="nx">endTime</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">regex</span><span class="p">(</span><span class="sr">/^\d{2}:\d{2}$/</span><span class="p">).</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-43-7"><a id="__codelineno-43-7" name="__codelineno-43-7" href="#__codelineno-43-7"></a><span class="w"> </span><span class="nx">location</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">nullable</span><span class="p">().</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-43-8"><a id="__codelineno-43-8" name="__codelineno-43-8" href="#__codelineno-43-8"></a><span class="w"> </span><span class="nx">maxVolunteers</span><span class="o">:</span><span class="w"> </span><span class="kt">z.number</span><span class="p">().</span><span class="kr">int</span><span class="p">().</span><span class="nx">min</span><span class="p">(</span><span class="mf">1</span><span class="p">).</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-43-9"><a id="__codelineno-43-9" name="__codelineno-43-9" href="#__codelineno-43-9"></a><span class="w"> </span><span class="nx">isPublic</span><span class="o">:</span><span class="w"> </span><span class="kt">z.boolean</span><span class="p">().</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-43-10"><a id="__codelineno-43-10" name="__codelineno-43-10" href="#__codelineno-43-10"></a><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="kt">z.nativeEnum</span><span class="p">(</span><span class="nx">ShiftStatus</span><span class="p">).</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-43-11"><a id="__codelineno-43-11" name="__codelineno-43-11" href="#__codelineno-43-11"></a><span class="w"> </span><span class="nx">cutId</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">nullable</span><span class="p">().</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-43-12"><a id="__codelineno-43-12" name="__codelineno-43-12" href="#__codelineno-43-12"></a><span class="p">});</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Partial Updates:</strong></p>
|
||
<p>All fields optional. Only provided fields are updated.</p>
|
||
<hr />
|
||
<h3 id="public-signup-schema">Public Signup Schema<a class="headerlink" href="#public-signup-schema" title="Permanent link">¶</a></h3>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-44-1"><a id="__codelineno-44-1" name="__codelineno-44-1" href="#__codelineno-44-1"></a><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">publicSignupSchema</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">z</span><span class="p">.</span><span class="nx">object</span><span class="p">({</span>
|
||
</span><span id="__span-44-2"><a id="__codelineno-44-2" name="__codelineno-44-2" href="#__codelineno-44-2"></a><span class="w"> </span><span class="nx">email</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">email</span><span class="p">(</span><span class="s1">'Valid email is required'</span><span class="p">),</span>
|
||
</span><span id="__span-44-3"><a id="__codelineno-44-3" name="__codelineno-44-3" href="#__codelineno-44-3"></a><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">min</span><span class="p">(</span><span class="mf">1</span><span class="p">,</span><span class="w"> </span><span class="s1">'Name is required'</span><span class="p">),</span>
|
||
</span><span id="__span-44-4"><a id="__codelineno-44-4" name="__codelineno-44-4" href="#__codelineno-44-4"></a><span class="w"> </span><span class="nx">phone</span><span class="o">:</span><span class="w"> </span><span class="kt">z.string</span><span class="p">().</span><span class="nx">optional</span><span class="p">(),</span>
|
||
</span><span id="__span-44-5"><a id="__codelineno-44-5" name="__codelineno-44-5" href="#__codelineno-44-5"></a><span class="p">});</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Example Valid Input:</strong></p>
|
||
<div class="language-json highlight"><pre><span></span><code><span id="__span-45-1"><a id="__codelineno-45-1" name="__codelineno-45-1" href="#__codelineno-45-1"></a><span class="p">{</span>
|
||
</span><span id="__span-45-2"><a id="__codelineno-45-2" name="__codelineno-45-2" href="#__codelineno-45-2"></a><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"volunteer@example.com"</span><span class="p">,</span>
|
||
</span><span id="__span-45-3"><a id="__codelineno-45-3" name="__codelineno-45-3" href="#__codelineno-45-3"></a><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jane Volunteer"</span><span class="p">,</span>
|
||
</span><span id="__span-45-4"><a id="__codelineno-45-4" name="__codelineno-45-4" href="#__codelineno-45-4"></a><span class="w"> </span><span class="nt">"phone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"+1234567890"</span>
|
||
</span><span id="__span-45-5"><a id="__codelineno-45-5" name="__codelineno-45-5" href="#__codelineno-45-5"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h2 id="code-examples">Code Examples<a class="headerlink" href="#code-examples" title="Permanent link">¶</a></h2>
|
||
<h3 id="admin-create-shift-with-cut-association">Admin: Create Shift with Cut Association<a class="headerlink" href="#admin-create-shift-with-cut-association" title="Permanent link">¶</a></h3>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-46-1"><a id="__codelineno-46-1" name="__codelineno-46-1" href="#__codelineno-46-1"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">api</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'@/lib/api'</span><span class="p">;</span>
|
||
</span><span id="__span-46-2"><a id="__codelineno-46-2" name="__codelineno-46-2" href="#__codelineno-46-2"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">message</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'antd'</span><span class="p">;</span>
|
||
</span><span id="__span-46-3"><a id="__codelineno-46-3" name="__codelineno-46-3" href="#__codelineno-46-3"></a>
|
||
</span><span id="__span-46-4"><a id="__codelineno-46-4" name="__codelineno-46-4" href="#__codelineno-46-4"></a><span class="kd">const</span><span class="w"> </span><span class="nx">createShift</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-46-5"><a id="__codelineno-46-5" name="__codelineno-46-5" href="#__codelineno-46-5"></a><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-46-6"><a id="__codelineno-46-6" name="__codelineno-46-6" href="#__codelineno-46-6"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">api</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'/api/map/shifts'</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-46-7"><a id="__codelineno-46-7" name="__codelineno-46-7" href="#__codelineno-46-7"></a><span class="w"> </span><span class="nx">title</span><span class="o">:</span><span class="w"> </span><span class="s1">'Door Knocking — Ward 5'</span><span class="p">,</span>
|
||
</span><span id="__span-46-8"><a id="__codelineno-46-8" name="__codelineno-46-8" href="#__codelineno-46-8"></a><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="s1">'Canvassing residential areas in Ward 5. Meet at campaign office.'</span><span class="p">,</span>
|
||
</span><span id="__span-46-9"><a id="__codelineno-46-9" name="__codelineno-46-9" href="#__codelineno-46-9"></a><span class="w"> </span><span class="nx">date</span><span class="o">:</span><span class="w"> </span><span class="s1">'2026-02-15'</span><span class="p">,</span>
|
||
</span><span id="__span-46-10"><a id="__codelineno-46-10" name="__codelineno-46-10" href="#__codelineno-46-10"></a><span class="w"> </span><span class="nx">startTime</span><span class="o">:</span><span class="w"> </span><span class="s1">'10:00'</span><span class="p">,</span>
|
||
</span><span id="__span-46-11"><a id="__codelineno-46-11" name="__codelineno-46-11" href="#__codelineno-46-11"></a><span class="w"> </span><span class="nx">endTime</span><span class="o">:</span><span class="w"> </span><span class="s1">'14:00'</span><span class="p">,</span>
|
||
</span><span id="__span-46-12"><a id="__codelineno-46-12" name="__codelineno-46-12" href="#__codelineno-46-12"></a><span class="w"> </span><span class="nx">location</span><span class="o">:</span><span class="w"> </span><span class="s1">'123 Main St (Campaign Office)'</span><span class="p">,</span>
|
||
</span><span id="__span-46-13"><a id="__codelineno-46-13" name="__codelineno-46-13" href="#__codelineno-46-13"></a><span class="w"> </span><span class="nx">maxVolunteers</span><span class="o">:</span><span class="w"> </span><span class="kt">15</span><span class="p">,</span>
|
||
</span><span id="__span-46-14"><a id="__codelineno-46-14" name="__codelineno-46-14" href="#__codelineno-46-14"></a><span class="w"> </span><span class="nx">isPublic</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
|
||
</span><span id="__span-46-15"><a id="__codelineno-46-15" name="__codelineno-46-15" href="#__codelineno-46-15"></a><span class="w"> </span><span class="nx">cutId</span><span class="o">:</span><span class="w"> </span><span class="s1">'clx0987654321'</span><span class="p">,</span><span class="w"> </span><span class="c1">// Associate with cut</span>
|
||
</span><span id="__span-46-16"><a id="__codelineno-46-16" name="__codelineno-46-16" href="#__codelineno-46-16"></a><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-46-17"><a id="__codelineno-46-17" name="__codelineno-46-17" href="#__codelineno-46-17"></a>
|
||
</span><span id="__span-46-18"><a id="__codelineno-46-18" name="__codelineno-46-18" href="#__codelineno-46-18"></a><span class="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">success</span><span class="p">(</span><span class="sb">`Shift created: </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">title</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
|
||
</span><span id="__span-46-19"><a id="__codelineno-46-19" name="__codelineno-46-19" href="#__codelineno-46-19"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">data</span><span class="p">;</span>
|
||
</span><span id="__span-46-20"><a id="__codelineno-46-20" name="__codelineno-46-20" href="#__codelineno-46-20"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-46-21"><a id="__codelineno-46-21" name="__codelineno-46-21" href="#__codelineno-46-21"></a><span class="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'Failed to create shift'</span><span class="p">);</span>
|
||
</span><span id="__span-46-22"><a id="__codelineno-46-22" name="__codelineno-46-22" href="#__codelineno-46-22"></a><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="nx">error</span><span class="p">;</span>
|
||
</span><span id="__span-46-23"><a id="__codelineno-46-23" name="__codelineno-46-23" href="#__codelineno-46-23"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-46-24"><a id="__codelineno-46-24" name="__codelineno-46-24" href="#__codelineno-46-24"></a><span class="p">};</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="volunteer-sign-up-for-shift">Volunteer: Sign Up for Shift<a class="headerlink" href="#volunteer-sign-up-for-shift" title="Permanent link">¶</a></h3>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-47-1"><a id="__codelineno-47-1" name="__codelineno-47-1" href="#__codelineno-47-1"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">api</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'@/lib/api'</span><span class="p">;</span>
|
||
</span><span id="__span-47-2"><a id="__codelineno-47-2" name="__codelineno-47-2" href="#__codelineno-47-2"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">message</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'antd'</span><span class="p">;</span>
|
||
</span><span id="__span-47-3"><a id="__codelineno-47-3" name="__codelineno-47-3" href="#__codelineno-47-3"></a>
|
||
</span><span id="__span-47-4"><a id="__codelineno-47-4" name="__codelineno-47-4" href="#__codelineno-47-4"></a><span class="kd">const</span><span class="w"> </span><span class="nx">signUpForShift</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="p">(</span><span class="nx">shiftId</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-47-5"><a id="__codelineno-47-5" name="__codelineno-47-5" href="#__codelineno-47-5"></a><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-47-6"><a id="__codelineno-47-6" name="__codelineno-47-6" href="#__codelineno-47-6"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">api</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="sb">`/api/map/shifts/volunteer/</span><span class="si">${</span><span class="nx">shiftId</span><span class="si">}</span><span class="sb">/signup`</span><span class="p">);</span>
|
||
</span><span id="__span-47-7"><a id="__codelineno-47-7" name="__codelineno-47-7" href="#__codelineno-47-7"></a>
|
||
</span><span id="__span-47-8"><a id="__codelineno-47-8" name="__codelineno-47-8" href="#__codelineno-47-8"></a><span class="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">success</span><span class="p">(</span><span class="s1">'Signed up successfully! Check your email for confirmation.'</span><span class="p">);</span>
|
||
</span><span id="__span-47-9"><a id="__codelineno-47-9" name="__codelineno-47-9" href="#__codelineno-47-9"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">data</span><span class="p">;</span>
|
||
</span><span id="__span-47-10"><a id="__codelineno-47-10" name="__codelineno-47-10" href="#__codelineno-47-10"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="o">:</span><span class="w"> </span><span class="kt">any</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-47-11"><a id="__codelineno-47-11" name="__codelineno-47-11" href="#__codelineno-47-11"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">response</span><span class="o">?</span><span class="p">.</span><span class="nx">data</span><span class="o">?</span><span class="p">.</span><span class="nx">code</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">'SHIFT_FULL'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-47-12"><a id="__codelineno-47-12" name="__codelineno-47-12" href="#__codelineno-47-12"></a><span class="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'This shift is full'</span><span class="p">);</span>
|
||
</span><span id="__span-47-13"><a id="__codelineno-47-13" name="__codelineno-47-13" href="#__codelineno-47-13"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">response</span><span class="o">?</span><span class="p">.</span><span class="nx">data</span><span class="o">?</span><span class="p">.</span><span class="nx">code</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">'DUPLICATE_SIGNUP'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-47-14"><a id="__codelineno-47-14" name="__codelineno-47-14" href="#__codelineno-47-14"></a><span class="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">warning</span><span class="p">(</span><span class="s1">'You are already signed up for this shift'</span><span class="p">);</span>
|
||
</span><span id="__span-47-15"><a id="__codelineno-47-15" name="__codelineno-47-15" href="#__codelineno-47-15"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-47-16"><a id="__codelineno-47-16" name="__codelineno-47-16" href="#__codelineno-47-16"></a><span class="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'Failed to sign up'</span><span class="p">);</span>
|
||
</span><span id="__span-47-17"><a id="__codelineno-47-17" name="__codelineno-47-17" href="#__codelineno-47-17"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-47-18"><a id="__codelineno-47-18" name="__codelineno-47-18" href="#__codelineno-47-18"></a><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="nx">error</span><span class="p">;</span>
|
||
</span><span id="__span-47-19"><a id="__codelineno-47-19" name="__codelineno-47-19" href="#__codelineno-47-19"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-47-20"><a id="__codelineno-47-20" name="__codelineno-47-20" href="#__codelineno-47-20"></a><span class="p">};</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="public-sign-up-without-account">Public: Sign Up Without Account<a class="headerlink" href="#public-sign-up-without-account" title="Permanent link">¶</a></h3>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-48-1"><a id="__codelineno-48-1" name="__codelineno-48-1" href="#__codelineno-48-1"></a><span class="k">import</span><span class="w"> </span><span class="nx">axios</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'axios'</span><span class="p">;</span>
|
||
</span><span id="__span-48-2"><a id="__codelineno-48-2" name="__codelineno-48-2" href="#__codelineno-48-2"></a>
|
||
</span><span id="__span-48-3"><a id="__codelineno-48-3" name="__codelineno-48-3" href="#__codelineno-48-3"></a><span class="kd">const</span><span class="w"> </span><span class="nx">publicSignup</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="p">(</span><span class="nx">shiftId</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">formData</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">email</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">;</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">;</span><span class="w"> </span><span class="nx">phone?</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-48-4"><a id="__codelineno-48-4" name="__codelineno-48-4" href="#__codelineno-48-4"></a><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-48-5"><a id="__codelineno-48-5" name="__codelineno-48-5" href="#__codelineno-48-5"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">axios</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span>
|
||
</span><span id="__span-48-6"><a id="__codelineno-48-6" name="__codelineno-48-6" href="#__codelineno-48-6"></a><span class="w"> </span><span class="sb">`/api/map/shifts/public/</span><span class="si">${</span><span class="nx">shiftId</span><span class="si">}</span><span class="sb">/signup`</span><span class="p">,</span>
|
||
</span><span id="__span-48-7"><a id="__codelineno-48-7" name="__codelineno-48-7" href="#__codelineno-48-7"></a><span class="w"> </span><span class="nx">formData</span>
|
||
</span><span id="__span-48-8"><a id="__codelineno-48-8" name="__codelineno-48-8" href="#__codelineno-48-8"></a><span class="w"> </span><span class="p">);</span>
|
||
</span><span id="__span-48-9"><a id="__codelineno-48-9" name="__codelineno-48-9" href="#__codelineno-48-9"></a>
|
||
</span><span id="__span-48-10"><a id="__codelineno-48-10" name="__codelineno-48-10" href="#__codelineno-48-10"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">isNewUser</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-48-11"><a id="__codelineno-48-11" name="__codelineno-48-11" href="#__codelineno-48-11"></a><span class="w"> </span><span class="nx">alert</span><span class="p">(</span><span class="sb">`Account created! Check your email for your temporary password.`</span><span class="p">);</span>
|
||
</span><span id="__span-48-12"><a id="__codelineno-48-12" name="__codelineno-48-12" href="#__codelineno-48-12"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-48-13"><a id="__codelineno-48-13" name="__codelineno-48-13" href="#__codelineno-48-13"></a><span class="w"> </span><span class="nx">alert</span><span class="p">(</span><span class="s1">'Signed up successfully!'</span><span class="p">);</span>
|
||
</span><span id="__span-48-14"><a id="__codelineno-48-14" name="__codelineno-48-14" href="#__codelineno-48-14"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-48-15"><a id="__codelineno-48-15" name="__codelineno-48-15" href="#__codelineno-48-15"></a>
|
||
</span><span id="__span-48-16"><a id="__codelineno-48-16" name="__codelineno-48-16" href="#__codelineno-48-16"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">data</span><span class="p">;</span>
|
||
</span><span id="__span-48-17"><a id="__codelineno-48-17" name="__codelineno-48-17" href="#__codelineno-48-17"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="o">:</span><span class="w"> </span><span class="kt">any</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-48-18"><a id="__codelineno-48-18" name="__codelineno-48-18" href="#__codelineno-48-18"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">response</span><span class="o">?</span><span class="p">.</span><span class="nx">status</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">429</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-48-19"><a id="__codelineno-48-19" name="__codelineno-48-19" href="#__codelineno-48-19"></a><span class="w"> </span><span class="nx">alert</span><span class="p">(</span><span class="s1">'Too many signups. Please try again in a minute.'</span><span class="p">);</span>
|
||
</span><span id="__span-48-20"><a id="__codelineno-48-20" name="__codelineno-48-20" href="#__codelineno-48-20"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">response</span><span class="o">?</span><span class="p">.</span><span class="nx">data</span><span class="o">?</span><span class="p">.</span><span class="nx">code</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">'SHIFT_FULL'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-48-21"><a id="__codelineno-48-21" name="__codelineno-48-21" href="#__codelineno-48-21"></a><span class="w"> </span><span class="nx">alert</span><span class="p">(</span><span class="s1">'This shift is full'</span><span class="p">);</span>
|
||
</span><span id="__span-48-22"><a id="__codelineno-48-22" name="__codelineno-48-22" href="#__codelineno-48-22"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-48-23"><a id="__codelineno-48-23" name="__codelineno-48-23" href="#__codelineno-48-23"></a><span class="w"> </span><span class="nx">alert</span><span class="p">(</span><span class="s1">'Failed to sign up'</span><span class="p">);</span>
|
||
</span><span id="__span-48-24"><a id="__codelineno-48-24" name="__codelineno-48-24" href="#__codelineno-48-24"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-48-25"><a id="__codelineno-48-25" name="__codelineno-48-25" href="#__codelineno-48-25"></a><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="nx">error</span><span class="p">;</span>
|
||
</span><span id="__span-48-26"><a id="__codelineno-48-26" name="__codelineno-48-26" href="#__codelineno-48-26"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-48-27"><a id="__codelineno-48-27" name="__codelineno-48-27" href="#__codelineno-48-27"></a><span class="p">};</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="admin-email-all-volunteers">Admin: Email All Volunteers<a class="headerlink" href="#admin-email-all-volunteers" title="Permanent link">¶</a></h3>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-49-1"><a id="__codelineno-49-1" name="__codelineno-49-1" href="#__codelineno-49-1"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">api</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'@/lib/api'</span><span class="p">;</span>
|
||
</span><span id="__span-49-2"><a id="__codelineno-49-2" name="__codelineno-49-2" href="#__codelineno-49-2"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">message</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'antd'</span><span class="p">;</span>
|
||
</span><span id="__span-49-3"><a id="__codelineno-49-3" name="__codelineno-49-3" href="#__codelineno-49-3"></a>
|
||
</span><span id="__span-49-4"><a id="__codelineno-49-4" name="__codelineno-49-4" href="#__codelineno-49-4"></a><span class="kd">const</span><span class="w"> </span><span class="nx">emailAllVolunteers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="p">(</span><span class="nx">shiftId</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-49-5"><a id="__codelineno-49-5" name="__codelineno-49-5" href="#__codelineno-49-5"></a><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-49-6"><a id="__codelineno-49-6" name="__codelineno-49-6" href="#__codelineno-49-6"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">api</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="sb">`/api/map/shifts/</span><span class="si">${</span><span class="nx">shiftId</span><span class="si">}</span><span class="sb">/email-details`</span><span class="p">);</span>
|
||
</span><span id="__span-49-7"><a id="__codelineno-49-7" name="__codelineno-49-7" href="#__codelineno-49-7"></a>
|
||
</span><span id="__span-49-8"><a id="__codelineno-49-8" name="__codelineno-49-8" href="#__codelineno-49-8"></a><span class="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">success</span><span class="p">(</span><span class="sb">`Sent </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">sent</span><span class="si">}</span><span class="sb"> emails successfully. </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">failed</span><span class="si">}</span><span class="sb"> failed.`</span><span class="p">);</span>
|
||
</span><span id="__span-49-9"><a id="__codelineno-49-9" name="__codelineno-49-9" href="#__codelineno-49-9"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">data</span><span class="p">;</span>
|
||
</span><span id="__span-49-10"><a id="__codelineno-49-10" name="__codelineno-49-10" href="#__codelineno-49-10"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-49-11"><a id="__codelineno-49-11" name="__codelineno-49-11" href="#__codelineno-49-11"></a><span class="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'Failed to send emails'</span><span class="p">);</span>
|
||
</span><span id="__span-49-12"><a id="__codelineno-49-12" name="__codelineno-49-12" href="#__codelineno-49-12"></a><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="nx">error</span><span class="p">;</span>
|
||
</span><span id="__span-49-13"><a id="__codelineno-49-13" name="__codelineno-49-13" href="#__codelineno-49-13"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-49-14"><a id="__codelineno-49-14" name="__codelineno-49-14" href="#__codelineno-49-14"></a><span class="p">};</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h2 id="frontend-integration">Frontend Integration<a class="headerlink" href="#frontend-integration" title="Permanent link">¶</a></h2>
|
||
<p>The ShiftsPage component (<code>admin/src/pages/ShiftsPage.tsx</code>) provides:</p>
|
||
<ul>
|
||
<li><strong>Paginated shifts table</strong> with search and status filter</li>
|
||
<li><strong>Cut association dropdown</strong> (optional, for canvassing shifts)</li>
|
||
<li><strong>Capacity badges</strong> (8/15 with OPEN/FULL status)</li>
|
||
<li><strong>Create shift modal</strong> with date/time pickers</li>
|
||
<li><strong>Edit shift modal</strong> (pre-populated form)</li>
|
||
<li><strong>Delete confirmation modal</strong></li>
|
||
<li><strong>Signups drawer</strong> (shows volunteers, email all button, remove signup)</li>
|
||
<li><strong>Public/private toggle</strong> (controls <code>isPublic</code> flag)</li>
|
||
<li><strong>Status badges</strong> (OPEN=green, FULL=orange, CANCELLED=red)</li>
|
||
</ul>
|
||
<p><strong>State Management:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-50-1"><a id="__codelineno-50-1" name="__codelineno-50-1" href="#__codelineno-50-1"></a><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">shifts</span><span class="p">,</span><span class="w"> </span><span class="nx">setShifts</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useState</span><span class="o"><</span><span class="nx">Shift</span><span class="p">[]</span><span class="o">></span><span class="p">([]);</span>
|
||
</span><span id="__span-50-2"><a id="__codelineno-50-2" name="__codelineno-50-2" href="#__codelineno-50-2"></a><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">pagination</span><span class="p">,</span><span class="w"> </span><span class="nx">setPagination</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useState</span><span class="p">({</span><span class="w"> </span><span class="nx">page</span><span class="o">:</span><span class="w"> </span><span class="kt">1</span><span class="p">,</span><span class="w"> </span><span class="nx">limit</span><span class="o">:</span><span class="w"> </span><span class="kt">20</span><span class="p">,</span><span class="w"> </span><span class="nx">total</span><span class="o">:</span><span class="w"> </span><span class="kt">0</span><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-50-3"><a id="__codelineno-50-3" name="__codelineno-50-3" href="#__codelineno-50-3"></a><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">filters</span><span class="p">,</span><span class="w"> </span><span class="nx">setFilters</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useState</span><span class="p">({</span><span class="w"> </span><span class="nx">search</span><span class="o">:</span><span class="w"> </span><span class="s1">''</span><span class="p">,</span><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="kt">null</span><span class="p">,</span><span class="w"> </span><span class="nx">upcoming</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-50-4"><a id="__codelineno-50-4" name="__codelineno-50-4" href="#__codelineno-50-4"></a><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">signupsDrawerOpen</span><span class="p">,</span><span class="w"> </span><span class="nx">setSignupsDrawerOpen</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useState</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
|
||
</span><span id="__span-50-5"><a id="__codelineno-50-5" name="__codelineno-50-5" href="#__codelineno-50-5"></a><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">selectedShift</span><span class="p">,</span><span class="w"> </span><span class="nx">setSelectedShift</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useState</span><span class="o"><</span><span class="nx">Shift</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">null</span><span class="o">></span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Volunteer Portal:</strong></p>
|
||
<p>The VolunteerShiftsPage component (<code>admin/src/pages/volunteer/VolunteerShiftsPage.tsx</code>) provides:</p>
|
||
<ul>
|
||
<li><strong>Upcoming shifts cards</strong> with shift details</li>
|
||
<li><strong>Signup status badges</strong> ("Signed Up" vs "Join Now")</li>
|
||
<li><strong>Capacity indicators</strong> (8/15 volunteers)</li>
|
||
<li><strong>Signup confirmation modal</strong></li>
|
||
<li><strong>Cancel signup functionality</strong></li>
|
||
<li><strong>My signups tab</strong> (shows user's confirmed signups)</li>
|
||
</ul>
|
||
<p><strong>Public Page:</strong></p>
|
||
<p>The ShiftsPage component (<code>admin/src/pages/public/ShiftsPage.tsx</code>) provides:</p>
|
||
<ul>
|
||
<li><strong>Public shift cards</strong> with date/time/location</li>
|
||
<li><strong>Signup modal</strong> (collects email, name, phone)</li>
|
||
<li><strong>Capacity indicators</strong></li>
|
||
<li><strong>Status badges</strong></li>
|
||
<li><strong>Responsive grid layout</strong></li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="performance-considerations">Performance Considerations<a class="headerlink" href="#performance-considerations" title="Permanent link">¶</a></h2>
|
||
<h3 id="capacity-tracking">Capacity Tracking<a class="headerlink" href="#capacity-tracking" title="Permanent link">¶</a></h3>
|
||
<p>The <code>currentVolunteers</code> field is denormalized for performance:</p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-51-1"><a id="__codelineno-51-1" name="__codelineno-51-1" href="#__codelineno-51-1"></a><span class="c1">// Instead of counting signups on every query:</span>
|
||
</span><span id="__span-51-2"><a id="__codelineno-51-2" name="__codelineno-51-2" href="#__codelineno-51-2"></a><span class="kd">const</span><span class="w"> </span><span class="nx">count</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">shiftSignup</span><span class="p">.</span><span class="nx">count</span><span class="p">({</span><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">shiftId</span><span class="p">,</span><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="s1">'CONFIRMED'</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-51-3"><a id="__codelineno-51-3" name="__codelineno-51-3" href="#__codelineno-51-3"></a>
|
||
</span><span id="__span-51-4"><a id="__codelineno-51-4" name="__codelineno-51-4" href="#__codelineno-51-4"></a><span class="c1">// We maintain a counter:</span>
|
||
</span><span id="__span-51-5"><a id="__codelineno-51-5" name="__codelineno-51-5" href="#__codelineno-51-5"></a><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-51-6"><a id="__codelineno-51-6" name="__codelineno-51-6" href="#__codelineno-51-6"></a><span class="w"> </span><span class="nx">currentVolunteers</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">increment</span><span class="o">:</span><span class="w"> </span><span class="kt">1</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="c1">// On signup</span>
|
||
</span><span id="__span-51-7"><a id="__codelineno-51-7" name="__codelineno-51-7" href="#__codelineno-51-7"></a><span class="w"> </span><span class="nx">currentVolunteers</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">decrement</span><span class="o">:</span><span class="w"> </span><span class="kt">1</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="c1">// On cancel</span>
|
||
</span><span id="__span-51-8"><a id="__codelineno-51-8" name="__codelineno-51-8" href="#__codelineno-51-8"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Pros:</strong></p>
|
||
<ul>
|
||
<li>No joins or aggregations needed for shift listings</li>
|
||
<li>Instant capacity checks</li>
|
||
<li>Fast status filtering</li>
|
||
</ul>
|
||
<p><strong>Cons:</strong></p>
|
||
<ul>
|
||
<li>Must maintain consistency in transactions</li>
|
||
<li>Risk of drift if transactions fail</li>
|
||
</ul>
|
||
<p><strong>Consistency Checks:</strong></p>
|
||
<p>Run periodic reconciliation:</p>
|
||
<div class="language-sql highlight"><pre><span></span><code><span id="__span-52-1"><a id="__codelineno-52-1" name="__codelineno-52-1" href="#__codelineno-52-1"></a><span class="k">UPDATE</span><span class="w"> </span><span class="n">shifts</span>
|
||
</span><span id="__span-52-2"><a id="__codelineno-52-2" name="__codelineno-52-2" href="#__codelineno-52-2"></a><span class="k">SET</span><span class="w"> </span><span class="ss">"currentVolunteers"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span>
|
||
</span><span id="__span-52-3"><a id="__codelineno-52-3" name="__codelineno-52-3" href="#__codelineno-52-3"></a><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">shift_signups</span>
|
||
</span><span id="__span-52-4"><a id="__codelineno-52-4" name="__codelineno-52-4" href="#__codelineno-52-4"></a><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="ss">"shiftId"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">shifts</span><span class="p">.</span><span class="n">id</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'CONFIRMED'</span>
|
||
</span><span id="__span-52-5"><a id="__codelineno-52-5" name="__codelineno-52-5" href="#__codelineno-52-5"></a><span class="p">)</span>
|
||
</span><span id="__span-52-6"><a id="__codelineno-52-6" name="__codelineno-52-6" href="#__codelineno-52-6"></a><span class="k">WHERE</span><span class="w"> </span><span class="ss">"currentVolunteers"</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="p">(</span>
|
||
</span><span id="__span-52-7"><a id="__codelineno-52-7" name="__codelineno-52-7" href="#__codelineno-52-7"></a><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">shift_signups</span>
|
||
</span><span id="__span-52-8"><a id="__codelineno-52-8" name="__codelineno-52-8" href="#__codelineno-52-8"></a><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="ss">"shiftId"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">shifts</span><span class="p">.</span><span class="n">id</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'CONFIRMED'</span>
|
||
</span><span id="__span-52-9"><a id="__codelineno-52-9" name="__codelineno-52-9" href="#__codelineno-52-9"></a><span class="p">);</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="unique-constraint-performance">Unique Constraint Performance<a class="headerlink" href="#unique-constraint-performance" title="Permanent link">¶</a></h3>
|
||
<p>The <code>[shiftId, userEmail]</code> unique constraint enables fast duplicate checks:</p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-53-1"><a id="__codelineno-53-1" name="__codelineno-53-1" href="#__codelineno-53-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">existing</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">shiftSignup</span><span class="p">.</span><span class="nx">findUnique</span><span class="p">({</span>
|
||
</span><span id="__span-53-2"><a id="__codelineno-53-2" name="__codelineno-53-2" href="#__codelineno-53-2"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">shiftId_userEmail</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">shiftId</span><span class="p">,</span><span class="w"> </span><span class="nx">userEmail</span><span class="o">:</span><span class="w"> </span><span class="kt">data.email</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-53-3"><a id="__codelineno-53-3" name="__codelineno-53-3" href="#__codelineno-53-3"></a><span class="p">});</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Index Usage:</strong></p>
|
||
<ul>
|
||
<li>PostgreSQL uses the unique index for lookups</li>
|
||
<li>O(log n) lookup time</li>
|
||
<li>No full table scan</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="rate-limiting">Rate Limiting<a class="headerlink" href="#rate-limiting" title="Permanent link">¶</a></h3>
|
||
<p>The <code>shiftSignupRateLimit</code> middleware protects against signup spam:</p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-54-1"><a id="__codelineno-54-1" name="__codelineno-54-1" href="#__codelineno-54-1"></a><span class="c1">// From api/src/middleware/rate-limit.ts</span>
|
||
</span><span id="__span-54-2"><a id="__codelineno-54-2" name="__codelineno-54-2" href="#__codelineno-54-2"></a><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">shiftSignupRateLimit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">createRateLimiter</span><span class="p">({</span>
|
||
</span><span id="__span-54-3"><a id="__codelineno-54-3" name="__codelineno-54-3" href="#__codelineno-54-3"></a><span class="w"> </span><span class="nx">windowMs</span><span class="o">:</span><span class="w"> </span><span class="kt">60</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">1000</span><span class="p">,</span><span class="w"> </span><span class="c1">// 1 minute</span>
|
||
</span><span id="__span-54-4"><a id="__codelineno-54-4" name="__codelineno-54-4" href="#__codelineno-54-4"></a><span class="w"> </span><span class="nx">max</span><span class="o">:</span><span class="w"> </span><span class="kt">5</span><span class="p">,</span><span class="w"> </span><span class="c1">// 5 signups per minute</span>
|
||
</span><span id="__span-54-5"><a id="__codelineno-54-5" name="__codelineno-54-5" href="#__codelineno-54-5"></a><span class="w"> </span><span class="nx">message</span><span class="o">:</span><span class="w"> </span><span class="s1">'Too many signup requests. Please try again later.'</span><span class="p">,</span>
|
||
</span><span id="__span-54-6"><a id="__codelineno-54-6" name="__codelineno-54-6" href="#__codelineno-54-6"></a><span class="p">});</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Why 5/min?</strong></p>
|
||
<ul>
|
||
<li>Allows legitimate users to sign up for multiple shifts quickly</li>
|
||
<li>Prevents automated abuse</li>
|
||
<li>Balances UX with security</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="troubleshooting">Troubleshooting<a class="headerlink" href="#troubleshooting" title="Permanent link">¶</a></h2>
|
||
<h3 id="shift-status-not-updating-automatically">Shift Status Not Updating Automatically<a class="headerlink" href="#shift-status-not-updating-automatically" title="Permanent link">¶</a></h3>
|
||
<p><strong>Problem:</strong></p>
|
||
<p>Shift status stays FULL even after volunteer cancels.</p>
|
||
<p><strong>Diagnosis:</strong></p>
|
||
<p>Check transaction logic in <code>removeSignup</code>:</p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-55-1"><a id="__codelineno-55-1" name="__codelineno-55-1" href="#__codelineno-55-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">$transaction</span><span class="p">([</span>
|
||
</span><span id="__span-55-2"><a id="__codelineno-55-2" name="__codelineno-55-2" href="#__codelineno-55-2"></a><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">shiftSignup</span><span class="p">.</span><span class="nx">update</span><span class="p">({</span><span class="w"> </span><span class="cm">/* ... */</span><span class="w"> </span><span class="p">}),</span>
|
||
</span><span id="__span-55-3"><a id="__codelineno-55-3" name="__codelineno-55-3" href="#__codelineno-55-3"></a><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">shift</span><span class="p">.</span><span class="nx">update</span><span class="p">({</span>
|
||
</span><span id="__span-55-4"><a id="__codelineno-55-4" name="__codelineno-55-4" href="#__codelineno-55-4"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="w"> </span><span class="kt">signup.shiftId</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-55-5"><a id="__codelineno-55-5" name="__codelineno-55-5" href="#__codelineno-55-5"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-55-6"><a id="__codelineno-55-6" name="__codelineno-55-6" href="#__codelineno-55-6"></a><span class="w"> </span><span class="nx">currentVolunteers</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">decrement</span><span class="o">:</span><span class="w"> </span><span class="kt">1</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-55-7"><a id="__codelineno-55-7" name="__codelineno-55-7" href="#__codelineno-55-7"></a><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="kt">ShiftStatus.OPEN</span><span class="p">,</span><span class="w"> </span><span class="c1">// Always set to OPEN on cancel</span>
|
||
</span><span id="__span-55-8"><a id="__codelineno-55-8" name="__codelineno-55-8" href="#__codelineno-55-8"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-55-9"><a id="__codelineno-55-9" name="__codelineno-55-9" href="#__codelineno-55-9"></a><span class="w"> </span><span class="p">}),</span>
|
||
</span><span id="__span-55-10"><a id="__codelineno-55-10" name="__codelineno-55-10" href="#__codelineno-55-10"></a><span class="p">]);</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Solution:</strong></p>
|
||
<p>Status is always set to OPEN on cancel. If shift should remain FULL (e.g., still at capacity), check if another transaction occurred simultaneously.</p>
|
||
<hr />
|
||
<h3 id="duplicate-signups">Duplicate Signups<a class="headerlink" href="#duplicate-signups" title="Permanent link">¶</a></h3>
|
||
<p><strong>Problem:</strong></p>
|
||
<p>User signed up twice for same shift.</p>
|
||
<p><strong>Diagnosis:</strong></p>
|
||
<p>Check unique constraint enforcement:</p>
|
||
<div class="language-sql highlight"><pre><span></span><code><span id="__span-56-1"><a id="__codelineno-56-1" name="__codelineno-56-1" href="#__codelineno-56-1"></a><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">shift_signups</span>
|
||
</span><span id="__span-56-2"><a id="__codelineno-56-2" name="__codelineno-56-2" href="#__codelineno-56-2"></a><span class="k">WHERE</span><span class="w"> </span><span class="ss">"shiftId"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'clx1234567890'</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="ss">"userEmail"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'volunteer@example.com'</span><span class="p">;</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Possible Causes:</strong></p>
|
||
<ul>
|
||
<li>Constraint disabled</li>
|
||
<li>Race condition (two requests hit database before first commit)</li>
|
||
<li>Manual database insertion bypassing constraint</li>
|
||
</ul>
|
||
<p><strong>Solution:</strong></p>
|
||
<ul>
|
||
<li>Verify constraint exists: <code>\d shift_signups</code> in psql</li>
|
||
<li>Add application-level locking if race conditions persist:
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-57-1"><a id="__codelineno-57-1" name="__codelineno-57-1" href="#__codelineno-57-1"></a><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">$transaction</span><span class="p">([</span>
|
||
</span><span id="__span-57-2"><a id="__codelineno-57-2" name="__codelineno-57-2" href="#__codelineno-57-2"></a><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">shiftSignup</span><span class="p">.</span><span class="nx">findUnique</span><span class="p">({</span><span class="w"> </span><span class="cm">/* check */</span><span class="w"> </span><span class="p">}),</span>
|
||
</span><span id="__span-57-3"><a id="__codelineno-57-3" name="__codelineno-57-3" href="#__codelineno-57-3"></a><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">shiftSignup</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span><span class="w"> </span><span class="cm">/* create */</span><span class="w"> </span><span class="p">}),</span>
|
||
</span><span id="__span-57-4"><a id="__codelineno-57-4" name="__codelineno-57-4" href="#__codelineno-57-4"></a><span class="p">],</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">isolationLevel</span><span class="o">:</span><span class="w"> </span><span class="s1">'Serializable'</span><span class="w"> </span><span class="p">});</span>
|
||
</span></code></pre></div></li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="confirmation-emails-not-sending">Confirmation Emails Not Sending<a class="headerlink" href="#confirmation-emails-not-sending" title="Permanent link">¶</a></h3>
|
||
<p><strong>Problem:</strong></p>
|
||
<p>Volunteers sign up but don't receive confirmation emails.</p>
|
||
<p><strong>Diagnosis:</strong></p>
|
||
<p>Check email service logs:</p>
|
||
<div class="language-bash highlight"><pre><span></span><code><span id="__span-58-1"><a id="__codelineno-58-1" name="__codelineno-58-1" href="#__codelineno-58-1"></a>docker<span class="w"> </span>compose<span class="w"> </span>logs<span class="w"> </span>-f<span class="w"> </span>api<span class="w"> </span><span class="p">|</span><span class="w"> </span>grep<span class="w"> </span><span class="s2">"shift signup confirmation"</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Common Causes:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p><strong>MailHog mode enabled:</strong>
|
||
<div class="language-text highlight"><pre><span></span><code><span id="__span-59-1"><a id="__codelineno-59-1" name="__codelineno-59-1" href="#__codelineno-59-1"></a>EMAIL_TEST_MODE=true # Emails go to MailHog, not SMTP
|
||
</span></code></pre></div></p>
|
||
</li>
|
||
<li>
|
||
<p><strong>SMTP misconfiguration:</strong>
|
||
<div class="language-text highlight"><pre><span></span><code><span id="__span-60-1"><a id="__codelineno-60-1" name="__codelineno-60-1" href="#__codelineno-60-1"></a>SMTP_HOST=smtp.gmail.com
|
||
</span><span id="__span-60-2"><a id="__codelineno-60-2" name="__codelineno-60-2" href="#__codelineno-60-2"></a>SMTP_PORT=587
|
||
</span><span id="__span-60-3"><a id="__codelineno-60-3" name="__codelineno-60-3" href="#__codelineno-60-3"></a>SMTP_USER=your-email@gmail.com
|
||
</span><span id="__span-60-4"><a id="__codelineno-60-4" name="__codelineno-60-4" href="#__codelineno-60-4"></a>SMTP_PASSWORD=your-app-password # Must be app password, not account password
|
||
</span></code></pre></div></p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Template missing:</strong>
|
||
<div class="language-bash highlight"><pre><span></span><code><span id="__span-61-1"><a id="__codelineno-61-1" name="__codelineno-61-1" href="#__codelineno-61-1"></a><span class="c1"># Check template exists</span>
|
||
</span><span id="__span-61-2"><a id="__codelineno-61-2" name="__codelineno-61-2" href="#__codelineno-61-2"></a>ls<span class="w"> </span>api/src/templates/shift-signup-confirmation.html
|
||
</span><span id="__span-61-3"><a id="__codelineno-61-3" name="__codelineno-61-3" href="#__codelineno-61-3"></a>ls<span class="w"> </span>api/src/templates/shift-signup-confirmation.txt
|
||
</span></code></pre></div></p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Email service crash:</strong>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-62-1"><a id="__codelineno-62-1" name="__codelineno-62-1" href="#__codelineno-62-1"></a><span class="k">try</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-62-2"><a id="__codelineno-62-2" name="__codelineno-62-2" href="#__codelineno-62-2"></a><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">emailService</span><span class="p">.</span><span class="nx">sendEmail</span><span class="p">({</span><span class="w"> </span><span class="cm">/* ... */</span><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-62-3"><a id="__codelineno-62-3" name="__codelineno-62-3" href="#__codelineno-62-3"></a><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-62-4"><a id="__codelineno-62-4" name="__codelineno-62-4" href="#__codelineno-62-4"></a><span class="w"> </span><span class="nx">logger</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'Failed to send shift signup confirmation email:'</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">);</span>
|
||
</span><span id="__span-62-5"><a id="__codelineno-62-5" name="__codelineno-62-5" href="#__codelineno-62-5"></a><span class="w"> </span><span class="c1">// Signup succeeds even if email fails</span>
|
||
</span><span id="__span-62-6"><a id="__codelineno-62-6" name="__codelineno-62-6" href="#__codelineno-62-6"></a><span class="p">}</span>
|
||
</span></code></pre></div></p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>Solution:</strong></p>
|
||
<ul>
|
||
<li>Set <code>EMAIL_TEST_MODE=false</code> for production</li>
|
||
<li>Verify SMTP credentials</li>
|
||
<li>Ensure templates exist</li>
|
||
<li>Check email logs for detailed errors</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="temp-users-not-expiring">Temp Users Not Expiring<a class="headerlink" href="#temp-users-not-expiring" title="Permanent link">¶</a></h3>
|
||
<p><strong>Problem:</strong></p>
|
||
<p>TEMP users created via public signup still active long after shift.</p>
|
||
<p><strong>Diagnosis:</strong></p>
|
||
<p>Check <code>expiresAt</code> value:</p>
|
||
<div class="language-sql highlight"><pre><span></span><code><span id="__span-63-1"><a id="__codelineno-63-1" name="__codelineno-63-1" href="#__codelineno-63-1"></a><span class="k">SELECT</span><span class="w"> </span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">email</span><span class="p">,</span><span class="w"> </span><span class="k">role</span><span class="p">,</span><span class="w"> </span><span class="ss">"expiresAt"</span><span class="p">,</span><span class="w"> </span><span class="ss">"createdAt"</span>
|
||
</span><span id="__span-63-2"><a id="__codelineno-63-2" name="__codelineno-63-2" href="#__codelineno-63-2"></a><span class="k">FROM</span><span class="w"> </span><span class="n">users</span>
|
||
</span><span id="__span-63-3"><a id="__codelineno-63-3" name="__codelineno-63-3" href="#__codelineno-63-3"></a><span class="k">WHERE</span><span class="w"> </span><span class="k">role</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'TEMP'</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="ss">"expiresAt"</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">NOW</span><span class="p">()</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'ACTIVE'</span><span class="p">;</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Expected Behavior:</strong></p>
|
||
<ul>
|
||
<li><code>expiresAt</code> is set to shift date + 1 day</li>
|
||
<li>Expired users should be marked EXPIRED by auth middleware</li>
|
||
</ul>
|
||
<p><strong>Solution:</strong></p>
|
||
<p>Run cleanup script or add cron job:</p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-64-1"><a id="__codelineno-64-1" name="__codelineno-64-1" href="#__codelineno-64-1"></a><span class="c1">// Expire temp users</span>
|
||
</span><span id="__span-64-2"><a id="__codelineno-64-2" name="__codelineno-64-2" href="#__codelineno-64-2"></a><span class="k">await</span><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">updateMany</span><span class="p">({</span>
|
||
</span><span id="__span-64-3"><a id="__codelineno-64-3" name="__codelineno-64-3" href="#__codelineno-64-3"></a><span class="w"> </span><span class="nx">where</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-64-4"><a id="__codelineno-64-4" name="__codelineno-64-4" href="#__codelineno-64-4"></a><span class="w"> </span><span class="nx">role</span><span class="o">:</span><span class="w"> </span><span class="kt">UserRole.TEMP</span><span class="p">,</span>
|
||
</span><span id="__span-64-5"><a id="__codelineno-64-5" name="__codelineno-64-5" href="#__codelineno-64-5"></a><span class="w"> </span><span class="nx">expiresAt</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">lte</span><span class="o">:</span><span class="w"> </span><span class="kt">new</span><span class="w"> </span><span class="nb">Date</span><span class="p">()</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-64-6"><a id="__codelineno-64-6" name="__codelineno-64-6" href="#__codelineno-64-6"></a><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">not</span><span class="o">:</span><span class="w"> </span><span class="kt">UserStatus.EXPIRED</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-64-7"><a id="__codelineno-64-7" name="__codelineno-64-7" href="#__codelineno-64-7"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-64-8"><a id="__codelineno-64-8" name="__codelineno-64-8" href="#__codelineno-64-8"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="kt">UserStatus.EXPIRED</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-64-9"><a id="__codelineno-64-9" name="__codelineno-64-9" href="#__codelineno-64-9"></a><span class="p">});</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h3 id="rate-limit-too-strict">Rate Limit Too Strict<a class="headerlink" href="#rate-limit-too-strict" title="Permanent link">¶</a></h3>
|
||
<p><strong>Problem:</strong></p>
|
||
<p>Users get rate-limited when legitimately signing up for multiple shifts.</p>
|
||
<p><strong>Diagnosis:</strong></p>
|
||
<p>Check rate limit config:</p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-65-1"><a id="__codelineno-65-1" name="__codelineno-65-1" href="#__codelineno-65-1"></a><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">shiftSignupRateLimit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">createRateLimiter</span><span class="p">({</span>
|
||
</span><span id="__span-65-2"><a id="__codelineno-65-2" name="__codelineno-65-2" href="#__codelineno-65-2"></a><span class="w"> </span><span class="nx">windowMs</span><span class="o">:</span><span class="w"> </span><span class="kt">60</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">1000</span><span class="p">,</span><span class="w"> </span><span class="c1">// 1 minute</span>
|
||
</span><span id="__span-65-3"><a id="__codelineno-65-3" name="__codelineno-65-3" href="#__codelineno-65-3"></a><span class="w"> </span><span class="nx">max</span><span class="o">:</span><span class="w"> </span><span class="kt">5</span><span class="p">,</span><span class="w"> </span><span class="c1">// 5 signups per minute</span>
|
||
</span><span id="__span-65-4"><a id="__codelineno-65-4" name="__codelineno-65-4" href="#__codelineno-65-4"></a><span class="p">});</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Solution:</strong></p>
|
||
<p>Increase limit if legitimate use case:</p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-66-1"><a id="__codelineno-66-1" name="__codelineno-66-1" href="#__codelineno-66-1"></a><span class="nx">max</span><span class="o">:</span><span class="w"> </span><span class="kt">10</span><span class="p">,</span><span class="w"> </span><span class="c1">// Allow 10 signups per minute</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Alternative:</strong></p>
|
||
<p>Whitelist admin IPs:</p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-67-1"><a id="__codelineno-67-1" name="__codelineno-67-1" href="#__codelineno-67-1"></a><span class="nx">skip</span><span class="o">:</span><span class="w"> </span><span class="p">(</span><span class="nx">req</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-67-2"><a id="__codelineno-67-2" name="__codelineno-67-2" href="#__codelineno-67-2"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">ip</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">req</span><span class="p">.</span><span class="nx">ip</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">req</span><span class="p">.</span><span class="nx">connection</span><span class="p">.</span><span class="nx">remoteAddress</span><span class="p">;</span>
|
||
</span><span id="__span-67-3"><a id="__codelineno-67-3" name="__codelineno-67-3" href="#__codelineno-67-3"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">[</span><span class="s1">'127.0.0.1'</span><span class="p">,</span><span class="w"> </span><span class="s1">'::1'</span><span class="p">,</span><span class="w"> </span><span class="nx">ADMIN_IP</span><span class="p">].</span><span class="nx">includes</span><span class="p">(</span><span class="nx">ip</span><span class="p">);</span>
|
||
</span><span id="__span-67-4"><a id="__codelineno-67-4" name="__codelineno-67-4" href="#__codelineno-67-4"></a><span class="p">},</span>
|
||
</span></code></pre></div>
|
||
<hr />
|
||
<h2 id="related-documentation">Related Documentation<a class="headerlink" href="#related-documentation" title="Permanent link">¶</a></h2>
|
||
<ul>
|
||
<li><a href="/v2/backend/modules/canvass.md">Canvass Module</a> - Volunteer canvassing system (uses <code>Shift.cutId</code>)</li>
|
||
<li><a href="/v2/backend/modules/cuts.md">Cuts Module</a> - Polygon management (cut association)</li>
|
||
<li><a href="/v2/backend/modules/users.md">Users Module</a> - User CRUD (temp user management)</li>
|
||
<li><a href="/v2/backend/modules/auth.md">Auth Module</a> - JWT authentication (temp user login)</li>
|
||
<li><a href="/v2/backend/modules/settings.md">Settings Module</a> - Organization name for emails</li>
|
||
<li><a href="/v2/backend/services/email.md">Email Service</a> - Email sending (confirmation emails)</li>
|
||
<li><a href="/v2/frontend/pages/admin/shifts-page.md">Frontend: ShiftsPage</a> - Admin shift management UI</li>
|
||
<li><a href="/v2/frontend/pages/volunteer/shifts-page.md">Frontend: Volunteer Portal</a> - Volunteer shift signup UI</li>
|
||
<li><a href="/v2/frontend/pages/public/shifts-page.md">Frontend: Public Shifts Page</a> - Public shift listings</li>
|
||
<li><a href="/v2/api-reference/shifts.md">API Reference: Shifts</a> - Complete endpoint reference</li>
|
||
<li><a href="/v2/user-guides/volunteer-manager-guide.md">User Guide: Volunteer Manager</a> - Managing volunteers</li>
|
||
<li><a href="/v2/troubleshooting/email-issues.md">Troubleshooting: Email Issues</a> - Email debugging guide</li>
|
||
</ul>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</article>
|
||
</div>
|
||
|
||
|
||
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
|
||
</div>
|
||
|
||
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
|
||
Back to top
|
||
</button>
|
||
|
||
</main>
|
||
|
||
<footer class="md-footer">
|
||
|
||
|
||
|
||
<nav class="md-footer__inner md-grid" aria-label="Footer" >
|
||
|
||
|
||
<a href="../locations/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Locations Module">
|
||
<div class="md-footer__button md-icon">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
|
||
</div>
|
||
<div class="md-footer__title">
|
||
<span class="md-footer__direction">
|
||
Previous
|
||
</span>
|
||
<div class="md-ellipsis">
|
||
Locations Module
|
||
</div>
|
||
</div>
|
||
</a>
|
||
|
||
|
||
|
||
<a href="../canvass/" class="md-footer__link md-footer__link--next" aria-label="Next: Canvass Module">
|
||
<div class="md-footer__title">
|
||
<span class="md-footer__direction">
|
||
Next
|
||
</span>
|
||
<div class="md-ellipsis">
|
||
Canvass Module
|
||
</div>
|
||
</div>
|
||
<div class="md-footer__button md-icon">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg>
|
||
</div>
|
||
</a>
|
||
|
||
</nav>
|
||
|
||
|
||
<div class="md-footer-meta md-typeset">
|
||
<div class="md-footer-meta__inner md-grid">
|
||
<div class="md-copyright">
|
||
|
||
<div class="md-copyright__highlight">
|
||
Copyright © 2024 The Bunker Operations – <a href="#__consent">Change cookie settings</a>
|
||
|
||
</div>
|
||
|
||
|
||
</div>
|
||
|
||
|
||
<div class="md-social">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="https://gitea.bnkops.com/admin" target="_blank" rel="noopener" title="Gitea Repository" class="md-social__link">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
|
||
</a>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="https://listmonk.bnkops.com/subscription/form" target="_blank" rel="noopener" title="Newsletter" class="md-social__link">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M536.4-26.3c9.8-3.5 20.6-1 28 6.3s9.8 18.2 6.3 28l-178 496.9c-5 13.9-18.1 23.1-32.8 23.1-14.2 0-27-8.6-32.3-21.7l-64.2-158c-4.5-11-2.5-23.6 5.2-32.6l94.5-112.4c5.1-6.1 4.7-15-.9-20.6s-14.6-6-20.6-.9l-112.4 94.3c-9.1 7.6-21.6 9.6-32.6 5.2L38.1 216.8c-13.1-5.3-21.7-18.1-21.7-32.3 0-14.7 9.2-27.8 23.1-32.8z"/></svg>
|
||
</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
</div>
|
||
<div class="md-dialog" data-md-component="dialog">
|
||
<div class="md-dialog__inner md-typeset"></div>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
<script id="__config" type="application/json">{"annotate": null, "base": "../../../..", "features": ["announce.dismiss", "content.action.edit", "content.action.view", "content.code.annotate", "content.code.copy", "content.tooltips", "navigation.expand", "navigation.footer", "navigation.indexes", "navigation.path", "navigation.prune", "navigation.sections", "navigation.tabs", "navigation.tabs.sticky", "navigation.top", "navigation.tracking", "search.highlight", "search.share", "search.suggest", "toc.follow"], "search": "../../../../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
|
||
|
||
|
||
<script src="../../../../assets/javascripts/bundle.79ae519e.min.js"></script>
|
||
|
||
<script src="../../../../javascripts/home.js"></script>
|
||
|
||
<script src="../../../../javascripts/github-widget.js"></script>
|
||
|
||
<script src="../../../../javascripts/gitea-widget.js"></script>
|
||
|
||
<script src="../../../../assets/js/env-config.js"></script>
|
||
|
||
<script src="../../../../assets/js/video-player.js"></script>
|
||
|
||
|
||
</body>
|
||
</html> |