6857 lines
274 KiB
HTML
6857 lines
274 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/features/influence/responses/">
|
||
|
||
|
||
<link rel="prev" href="../postal-codes/">
|
||
|
||
|
||
<link rel="next" href="../email-queue/">
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="icon" href="../../../../assets/favicon.png">
|
||
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.1">
|
||
|
||
|
||
|
||
<title>Responses - 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="Responses - 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/features/influence/responses.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/features/influence/responses/" />
|
||
<meta property="twitter:card" content="summary_large_image" />
|
||
<meta property="twitter:title" content="Responses - 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/features/influence/responses.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="#response-wall-system" 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">
|
||
|
||
Responses
|
||
|
||
</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--section md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_4" >
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../../backend/" 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="false">
|
||
<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--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../../backend/modules/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Modules
|
||
|
||
|
||
|
||
</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="../../../backend/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="../../../backend/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="../../../backend/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--active md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_7" checked>
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../../" 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="true">
|
||
<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--active md-nav__item--nested">
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_7_2" checked>
|
||
|
||
|
||
<div class="md-nav__link md-nav__container">
|
||
<a href="../" class="md-nav__link ">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Influence
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
<label class="md-nav__link " for="__nav_2_7_2" id="__nav_2_7_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_7_2_label" aria-expanded="true">
|
||
<label class="md-nav__title" for="__nav_2_7_2">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
|
||
|
||
Influence
|
||
|
||
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../campaigns/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Campaigns
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../representatives/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Representatives
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../postal-codes/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Postal Codes
|
||
|
||
|
||
|
||
</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">
|
||
|
||
|
||
Responses
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
<a href="./" class="md-nav__link md-nav__link--active">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Responses
|
||
|
||
|
||
|
||
</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="#architecture" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Architecture
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#database-models" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Database Models
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Database Models">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#response-model" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Response Model
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#responseupvote-model" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
ResponseUpvote Model
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</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" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin Endpoints
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-endpoints" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public Endpoints
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#configuration" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Configuration
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Configuration">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#environment-variables" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Environment Variables
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#campaign-feature-flags" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Campaign Feature Flags
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#upload-configuration" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Upload Configuration
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#admin-workflow" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin Workflow
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Admin Workflow">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#1-view-pending-responses" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
1. View Pending Responses
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#2-review-response-details" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
2. Review Response Details
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#3-approve-or-reject-response" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
3. Approve or Reject Response
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#4-bulk-moderation-actions" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
4. Bulk Moderation Actions
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-workflow" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public Workflow
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Public Workflow">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#1-submit-response" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
1. Submit Response
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#2-verify-email" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
2. Verify Email
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#3-view-response-wall" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
3. View Response Wall
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#4-upvote-response" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
4. Upvote Response
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#volunteer-workflow" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Volunteer Workflow
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</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="#backend-email-verification-token" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Backend: Email Verification Token
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#frontend-response-card-component" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Frontend: Response Card Component
|
||
|
||
</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="#verification-email-not-received" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Verification Email Not Received
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duplicate-upvotes" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Duplicate Upvotes
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#screenshot-upload-fails" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Screenshot Upload Fails
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</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="#query-optimization" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Query Optimization
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#caching-strategy" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Caching Strategy
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#screenshot-optimization" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Screenshot Optimization
|
||
|
||
</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>
|
||
|
||
<nav class="md-nav" aria-label="Related Documentation">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#backend-modules" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Backend Modules
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#frontend-pages" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Frontend Pages
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#database-models_1" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Database Models
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#guides" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Guides
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../email-queue/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Email Queue
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<a href="../../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="../../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="../../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="../../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="../../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="../../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="../../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="#architecture" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Architecture
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#database-models" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Database Models
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Database Models">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#response-model" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Response Model
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#responseupvote-model" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
ResponseUpvote Model
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</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" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin Endpoints
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-endpoints" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public Endpoints
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#configuration" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Configuration
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Configuration">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#environment-variables" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Environment Variables
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#campaign-feature-flags" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Campaign Feature Flags
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#upload-configuration" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Upload Configuration
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#admin-workflow" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Admin Workflow
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Admin Workflow">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#1-view-pending-responses" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
1. View Pending Responses
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#2-review-response-details" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
2. Review Response Details
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#3-approve-or-reject-response" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
3. Approve or Reject Response
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#4-bulk-moderation-actions" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
4. Bulk Moderation Actions
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#public-workflow" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Public Workflow
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Public Workflow">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#1-submit-response" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
1. Submit Response
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#2-verify-email" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
2. Verify Email
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#3-view-response-wall" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
3. View Response Wall
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#4-upvote-response" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
4. Upvote Response
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#volunteer-workflow" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Volunteer Workflow
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</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="#backend-email-verification-token" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Backend: Email Verification Token
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#frontend-response-card-component" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Frontend: Response Card Component
|
||
|
||
</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="#verification-email-not-received" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Verification Email Not Received
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duplicate-upvotes" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Duplicate Upvotes
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#screenshot-upload-fails" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Screenshot Upload Fails
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</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="#query-optimization" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Query Optimization
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#caching-strategy" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Caching Strategy
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#screenshot-optimization" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Screenshot Optimization
|
||
|
||
</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>
|
||
|
||
<nav class="md-nav" aria-label="Related Documentation">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#backend-modules" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Backend Modules
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#frontend-pages" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Frontend Pages
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#database-models_1" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Database Models
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#guides" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Guides
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</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">
|
||
Features
|
||
</span>
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-path__item">
|
||
<a href="../" class="md-path__link">
|
||
|
||
<span class="md-ellipsis">
|
||
Influence
|
||
</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/features/influence/responses.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/features/influence/responses.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="response-wall-system">Response Wall System<a class="headerlink" href="#response-wall-system" title="Permanent link">¶</a></h1>
|
||
<h2 id="overview">Overview<a class="headerlink" href="#overview" title="Permanent link">¶</a></h2>
|
||
<p>The response wall system allows campaign participants to share their advocacy actions publicly, creating social proof and encouraging further participation. It includes email verification, admin moderation, upvoting capabilities, and screenshot uploads to showcase genuine participation.</p>
|
||
<p><strong>Key Capabilities:</strong></p>
|
||
<ul>
|
||
<li><strong>Multiple response types</strong>: EMAIL, LETTER, PHONE_CALL, MEETING, SOCIAL_MEDIA, OTHER</li>
|
||
<li><strong>Email verification</strong>: Prevent spam with email confirmation</li>
|
||
<li><strong>Admin moderation</strong>: PENDING → APPROVED/REJECTED workflow</li>
|
||
<li><strong>Upvoting system</strong>: Community engagement with IP + user tracking</li>
|
||
<li><strong>Screenshot uploads</strong>: Visual proof of participation</li>
|
||
<li><strong>Public response wall</strong>: SEO-friendly public display</li>
|
||
<li><strong>Moderation dashboard</strong>: Admin tools for reviewing submissions</li>
|
||
</ul>
|
||
<p><strong>Use Cases:</strong></p>
|
||
<ul>
|
||
<li>Public display of campaign participation</li>
|
||
<li>Social proof for advocacy campaigns</li>
|
||
<li>Community engagement and sharing</li>
|
||
<li>Response verification and moderation</li>
|
||
<li>Campaign effectiveness metrics</li>
|
||
</ul>
|
||
<h2 id="architecture">Architecture<a class="headerlink" href="#architecture" title="Permanent link">¶</a></h2>
|
||
<pre class="mermaid"><code>graph TD
|
||
A[Public User] -->|Submit Response| B[ResponseWallPage]
|
||
B -->|POST /api/public/responses| C[Response Service]
|
||
C -->|Save| D[(Response Model)]
|
||
C -->|Send| E[Email Service]
|
||
E -->|Verification Email| F[User Inbox]
|
||
|
||
F -->|Click Link| G[Verify Endpoint]
|
||
G -->|Update| D
|
||
|
||
H[Admin User] -->|Review| I[ResponsesPage]
|
||
I -->|GET /api/responses| C
|
||
I -->|Approve/Reject| C
|
||
C -->|Update Status| D
|
||
|
||
J[Public User] -->|View Wall| K[ResponseWallPage]
|
||
K -->|GET /api/public/responses/:campaignId| C
|
||
C -->|Filter APPROVED| D
|
||
|
||
K -->|Upvote| L[Upvote Service]
|
||
L -->|Track| M[(ResponseUpvote Model)]
|
||
L -->|Increment| D
|
||
|
||
style D fill:#e1f5ff
|
||
style M fill:#e1f5ff
|
||
style E fill:#fff4e1</code></pre>
|
||
<p><strong>Flow Description:</strong></p>
|
||
<ol>
|
||
<li><strong>User submits response</strong> → Response service saves with PENDING status</li>
|
||
<li><strong>Verification email sent</strong> → User clicks link to verify email</li>
|
||
<li><strong>Email verified</strong> → Response marked as email verified</li>
|
||
<li><strong>Admin reviews</strong> → Moderates response (approve/reject)</li>
|
||
<li><strong>Response approved</strong> → Appears on public response wall</li>
|
||
<li><strong>Users upvote</strong> → Upvote service tracks votes, increments count</li>
|
||
<li><strong>Public views wall</strong> → Only approved responses displayed</li>
|
||
</ol>
|
||
<h2 id="database-models">Database Models<a class="headerlink" href="#database-models" title="Permanent link">¶</a></h2>
|
||
<h3 id="response-model">Response Model<a class="headerlink" href="#response-model" title="Permanent link">¶</a></h3>
|
||
<p>See <a href="../../database/models/response.md">Response Model Documentation</a> for full schema.</p>
|
||
<p><strong>Key Fields:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>id</code></td>
|
||
<td>String (UUID)</td>
|
||
<td>Primary key</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>campaignId</code></td>
|
||
<td>String</td>
|
||
<td>Associated campaign</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>responseType</code></td>
|
||
<td>Enum</td>
|
||
<td>EMAIL, LETTER, PHONE_CALL, MEETING, SOCIAL_MEDIA, OTHER</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>message</code></td>
|
||
<td>String</td>
|
||
<td>User's response message</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>screenshotUrl</code></td>
|
||
<td>String?</td>
|
||
<td>Uploaded screenshot URL</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>name</code></td>
|
||
<td>String</td>
|
||
<td>Submitter's name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>email</code></td>
|
||
<td>String</td>
|
||
<td>Submitter's email</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>postalCode</code></td>
|
||
<td>String?</td>
|
||
<td>Submitter's postal code</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>isEmailVerified</code></td>
|
||
<td>Boolean</td>
|
||
<td>Email verification status</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>status</code></td>
|
||
<td>Enum</td>
|
||
<td>PENDING, APPROVED, REJECTED</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>upvotes</code></td>
|
||
<td>Int</td>
|
||
<td>Number of upvotes</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>moderatedByUserId</code></td>
|
||
<td>String?</td>
|
||
<td>Admin who moderated</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>moderationNotes</code></td>
|
||
<td>String?</td>
|
||
<td>Admin notes</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Indexes:</strong></p>
|
||
<ul>
|
||
<li><code>campaignId, status</code> — For public wall queries</li>
|
||
<li><code>email, campaignId</code> — Prevent duplicate submissions</li>
|
||
<li><code>isEmailVerified</code> — Filter unverified responses</li>
|
||
</ul>
|
||
<h3 id="responseupvote-model">ResponseUpvote Model<a class="headerlink" href="#responseupvote-model" title="Permanent link">¶</a></h3>
|
||
<p>See <a href="../../database/models/response-upvote.md">ResponseUpvote Model Documentation</a> for full schema.</p>
|
||
<p><strong>Key Fields:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>id</code></td>
|
||
<td>String (UUID)</td>
|
||
<td>Primary key</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>responseId</code></td>
|
||
<td>String</td>
|
||
<td>Associated response</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>ipAddress</code></td>
|
||
<td>String?</td>
|
||
<td>Voter IP address</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>userId</code></td>
|
||
<td>String?</td>
|
||
<td>Voter user ID (if logged in)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Constraints:</strong></p>
|
||
<ul>
|
||
<li>Unique constraint on <code>responseId, ipAddress</code> — Prevent duplicate upvotes by IP</li>
|
||
<li>Unique constraint on <code>responseId, userId</code> — Prevent duplicate upvotes by user</li>
|
||
</ul>
|
||
<p><strong>Related Models:</strong></p>
|
||
<ul>
|
||
<li><a href="../../database/models/campaign.md">Campaign</a> — Campaign association</li>
|
||
<li><a href="../../database/models/user.md">User</a> — Moderation user</li>
|
||
</ul>
|
||
<h2 id="api-endpoints">API Endpoints<a class="headerlink" href="#api-endpoints" title="Permanent link">¶</a></h2>
|
||
<h3 id="admin-endpoints">Admin Endpoints<a class="headerlink" href="#admin-endpoints" title="Permanent link">¶</a></h3>
|
||
<p>See <a href="../../../backend/modules/responses/#endpoints">Responses Module API Reference</a> for full details.</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Method</th>
|
||
<th>Endpoint</th>
|
||
<th>Auth</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>GET</td>
|
||
<td><code>/api/responses</code></td>
|
||
<td>SUPER_ADMIN, INFLUENCE_ADMIN</td>
|
||
<td>List all responses (paginated, filterable)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>GET</td>
|
||
<td><code>/api/responses/:id</code></td>
|
||
<td>SUPER_ADMIN, INFLUENCE_ADMIN</td>
|
||
<td>Get response details</td>
|
||
</tr>
|
||
<tr>
|
||
<td>PATCH</td>
|
||
<td><code>/api/responses/:id/moderate</code></td>
|
||
<td>SUPER_ADMIN, INFLUENCE_ADMIN</td>
|
||
<td>Approve/reject response</td>
|
||
</tr>
|
||
<tr>
|
||
<td>DELETE</td>
|
||
<td><code>/api/responses/:id</code></td>
|
||
<td>SUPER_ADMIN</td>
|
||
<td>Delete response</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="public-endpoints">Public Endpoints<a class="headerlink" href="#public-endpoints" title="Permanent link">¶</a></h3>
|
||
<p>See <a href="../../../backend/modules/responses/#public-endpoints">Responses Module API Reference</a>.</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Method</th>
|
||
<th>Endpoint</th>
|
||
<th>Auth</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>GET</td>
|
||
<td><code>/api/public/responses/:campaignId</code></td>
|
||
<td>None</td>
|
||
<td>List approved responses for campaign</td>
|
||
</tr>
|
||
<tr>
|
||
<td>POST</td>
|
||
<td><code>/api/public/responses</code></td>
|
||
<td>None</td>
|
||
<td>Submit new response</td>
|
||
</tr>
|
||
<tr>
|
||
<td>GET</td>
|
||
<td><code>/api/public/responses/verify/:token</code></td>
|
||
<td>None</td>
|
||
<td>Verify email via token</td>
|
||
</tr>
|
||
<tr>
|
||
<td>POST</td>
|
||
<td><code>/api/public/responses/:id/upvote</code></td>
|
||
<td>None</td>
|
||
<td>Upvote response (IP/user tracked)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="configuration">Configuration<a class="headerlink" href="#configuration" title="Permanent link">¶</a></h2>
|
||
<h3 id="environment-variables">Environment Variables<a class="headerlink" href="#environment-variables" title="Permanent link">¶</a></h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Variable</th>
|
||
<th>Type</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>EMAIL_TEST_MODE</code></td>
|
||
<td>boolean</td>
|
||
<td>false</td>
|
||
<td>Send verification emails to MailHog</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>SMTP_FROM_EMAIL</code></td>
|
||
<td>string</td>
|
||
<td>-</td>
|
||
<td>Sender email for verification</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>SMTP_FROM_NAME</code></td>
|
||
<td>string</td>
|
||
<td>-</td>
|
||
<td>Sender name for verification</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>RESPONSE_VERIFICATION_URL</code></td>
|
||
<td>string</td>
|
||
<td>-</td>
|
||
<td>Base URL for verification links</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="campaign-feature-flags">Campaign Feature Flags<a class="headerlink" href="#campaign-feature-flags" title="Permanent link">¶</a></h3>
|
||
<p>Response wall behavior configured per campaign:</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Flag</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>showResponseWall</code></td>
|
||
<td>Enable response wall for campaign</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>requireEmailVerification</code></td>
|
||
<td>Require email verification before display</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>allowAnonymousResponses</code></td>
|
||
<td>Allow submissions without login</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="upload-configuration">Upload Configuration<a class="headerlink" href="#upload-configuration" title="Permanent link">¶</a></h3>
|
||
<p>Screenshots uploaded to <code>/uploads/responses/{responseId}/{filename}</code>.</p>
|
||
<p><strong>Limits:</strong>
|
||
- Max file size: 5MB
|
||
- Allowed formats: jpg, jpeg, png, gif, webp</p>
|
||
<h2 id="admin-workflow">Admin Workflow<a class="headerlink" href="#admin-workflow" title="Permanent link">¶</a></h2>
|
||
<h3 id="1-view-pending-responses">1. View Pending Responses<a class="headerlink" href="#1-view-pending-responses" title="Permanent link">¶</a></h3>
|
||
<p>[Screenshot: ResponsesPage with pending filter active]</p>
|
||
<p><strong>Steps:</strong></p>
|
||
<ol>
|
||
<li>Navigate to <strong>Influence > Responses</strong></li>
|
||
<li>Click <strong>Pending</strong> filter tab</li>
|
||
<li>View pending responses requiring moderation</li>
|
||
<li>Sort by submission date (newest first)</li>
|
||
</ol>
|
||
<p><strong>Code Example (ResponsesPage.tsx):</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">responses</span><span class="p">,</span><span class="w"> </span><span class="nx">setResponses</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">Response</span><span class="p">[]</span><span class="o">></span><span class="p">([]);</span>
|
||
</span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">filter</span><span class="p">,</span><span class="w"> </span><span class="nx">setFilter</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="s1">'all'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s1">'pending'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s1">'approved'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s1">'rejected'</span><span class="o">></span><span class="p">(</span><span class="s1">'pending'</span><span class="p">);</span>
|
||
</span><span id="__span-0-3"><a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
|
||
</span><span id="__span-0-4"><a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="nx">useEffect</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-0-5"><a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">fetchResponses</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-0-6"><a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">params</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">URLSearchParams</span><span class="p">();</span>
|
||
</span><span id="__span-0-7"><a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a>
|
||
</span><span id="__span-0-8"><a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">filter</span><span class="w"> </span><span class="o">!==</span><span class="w"> </span><span class="s1">'all'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-0-9"><a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="w"> </span><span class="nx">params</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s1">'status'</span><span class="p">,</span><span class="w"> </span><span class="nx">filter</span><span class="p">.</span><span class="nx">toUpperCase</span><span class="p">());</span>
|
||
</span><span id="__span-0-10"><a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-0-11"><a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a>
|
||
</span><span id="__span-0-12"><a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></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">get</span><span class="p">(</span><span class="sb">`/responses?</span><span class="si">${</span><span class="nx">params</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
|
||
</span><span id="__span-0-13"><a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a><span class="w"> </span><span class="nx">setResponses</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">responses</span><span class="p">);</span>
|
||
</span><span id="__span-0-14"><a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a><span class="w"> </span><span class="p">};</span>
|
||
</span><span id="__span-0-15"><a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>
|
||
</span><span id="__span-0-16"><a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a><span class="w"> </span><span class="nx">fetchResponses</span><span class="p">();</span>
|
||
</span><span id="__span-0-17"><a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a><span class="p">},</span><span class="w"> </span><span class="p">[</span><span class="nx">filter</span><span class="p">]);</span>
|
||
</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><span class="k">return</span><span class="w"> </span><span class="p">(</span>
|
||
</span><span id="__span-0-20"><a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="w"> </span><span class="o"><</span><span class="nx">Card</span><span class="o">></span>
|
||
</span><span id="__span-0-21"><a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="w"> </span><span class="o"><</span><span class="nx">Tabs</span><span class="w"> </span><span class="nx">activeKey</span><span class="o">=</span><span class="p">{</span><span class="nx">filter</span><span class="p">}</span><span class="w"> </span><span class="nx">onChange</span><span class="o">=</span><span class="p">{</span><span class="nx">setFilter</span><span class="p">}</span><span class="o">></span>
|
||
</span><span id="__span-0-22"><a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="w"> </span><span class="o"><</span><span class="nx">TabPane</span><span class="w"> </span><span class="nx">tab</span><span class="o">=</span><span class="s2">"Pending"</span><span class="w"> </span><span class="nx">key</span><span class="o">=</span><span class="s2">"pending"</span><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-0-23"><a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a><span class="w"> </span><span class="o"><</span><span class="nx">TabPane</span><span class="w"> </span><span class="nx">tab</span><span class="o">=</span><span class="s2">"Approved"</span><span class="w"> </span><span class="nx">key</span><span class="o">=</span><span class="s2">"approved"</span><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-0-24"><a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a><span class="w"> </span><span class="o"><</span><span class="nx">TabPane</span><span class="w"> </span><span class="nx">tab</span><span class="o">=</span><span class="s2">"Rejected"</span><span class="w"> </span><span class="nx">key</span><span class="o">=</span><span class="s2">"rejected"</span><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-0-25"><a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a><span class="w"> </span><span class="o"><</span><span class="nx">TabPane</span><span class="w"> </span><span class="nx">tab</span><span class="o">=</span><span class="s2">"All"</span><span class="w"> </span><span class="nx">key</span><span class="o">=</span><span class="s2">"all"</span><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-0-26"><a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a><span class="w"> </span><span class="o"><</span><span class="err">/Tabs></span>
|
||
</span><span id="__span-0-27"><a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>
|
||
</span><span id="__span-0-28"><a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="w"> </span><span class="o"><</span><span class="nx">Table</span><span class="w"> </span><span class="nx">dataSource</span><span class="o">=</span><span class="p">{</span><span class="nx">responses</span><span class="p">}</span><span class="w"> </span><span class="nx">columns</span><span class="o">=</span><span class="p">{</span><span class="nx">columns</span><span class="p">}</span><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-0-29"><a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="w"> </span><span class="o"><</span><span class="err">/Card></span>
|
||
</span><span id="__span-0-30"><a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a><span class="p">);</span>
|
||
</span></code></pre></div>
|
||
<h3 id="2-review-response-details">2. Review Response Details<a class="headerlink" href="#2-review-response-details" title="Permanent link">¶</a></h3>
|
||
<p>[Screenshot: Response detail drawer with full content]</p>
|
||
<p><strong>Steps:</strong></p>
|
||
<ol>
|
||
<li>Click <strong>View</strong> on response row</li>
|
||
<li>Review response details:</li>
|
||
<li>Campaign name</li>
|
||
<li>Response type</li>
|
||
<li>Submitter name and email</li>
|
||
<li>Message content</li>
|
||
<li>Screenshot (if uploaded)</li>
|
||
<li>Email verification status</li>
|
||
<li>Submission date</li>
|
||
<li>Check for spam/inappropriate content</li>
|
||
</ol>
|
||
<p><strong>Moderation Checklist:</strong></p>
|
||
<ul>
|
||
<li>✓ Message is genuine and relevant</li>
|
||
<li>✓ Screenshot matches claimed action (if provided)</li>
|
||
<li>✓ Email verified (if required by campaign)</li>
|
||
<li>✓ No profanity or inappropriate content</li>
|
||
<li>✓ Not duplicate submission</li>
|
||
</ul>
|
||
<h3 id="3-approve-or-reject-response">3. Approve or Reject Response<a class="headerlink" href="#3-approve-or-reject-response" title="Permanent link">¶</a></h3>
|
||
<p>[Screenshot: Response detail drawer with approve/reject buttons]</p>
|
||
<p><strong>Steps:</strong></p>
|
||
<ol>
|
||
<li>Click <strong>Approve</strong> or <strong>Reject</strong> button</li>
|
||
<li>Add moderation notes (optional but recommended)</li>
|
||
<li>Confirm action</li>
|
||
<li>Response status updated</li>
|
||
<li>If approved → appears on public response wall</li>
|
||
<li>If rejected → hidden from public, admin can view</li>
|
||
</ol>
|
||
<p><strong>Code Example (responses.service.ts):</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="k">async</span><span class="w"> </span><span class="nx">moderateResponse</span><span class="p">(</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="nx">responseId</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span>
|
||
</span><span id="__span-1-3"><a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="s1">'APPROVED'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s1">'REJECTED'</span><span class="p">,</span>
|
||
</span><span id="__span-1-4"><a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a><span class="w"> </span><span class="nx">moderatorUserId</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span>
|
||
</span><span id="__span-1-5"><a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a><span class="w"> </span><span class="nx">notes?</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span>
|
||
</span><span id="__span-1-6"><a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">Response</span><span class="o">></span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-1-7"><a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">response</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">prisma</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nx">update</span><span class="p">({</span>
|
||
</span><span id="__span-1-8"><a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></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">responseId</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-1-9"><a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></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-1-10"><a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a><span class="w"> </span><span class="nx">status</span><span class="p">,</span>
|
||
</span><span id="__span-1-11"><a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a><span class="w"> </span><span class="nx">moderatedByUserId</span><span class="o">:</span><span class="w"> </span><span class="kt">moderatorUserId</span><span class="p">,</span>
|
||
</span><span id="__span-1-12"><a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a><span class="w"> </span><span class="nx">moderationNotes</span><span class="o">:</span><span class="w"> </span><span class="kt">notes</span><span class="p">,</span>
|
||
</span><span id="__span-1-13"><a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a><span class="w"> </span><span class="nx">moderatedAt</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><span id="__span-1-14"><a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-1-15"><a id="__codelineno-1-15" name="__codelineno-1-15" href="#__codelineno-1-15"></a><span class="w"> </span><span class="nx">include</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-1-16"><a id="__codelineno-1-16" name="__codelineno-1-16" href="#__codelineno-1-16"></a><span class="w"> </span><span class="nx">campaign</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
|
||
</span><span id="__span-1-17"><a id="__codelineno-1-17" name="__codelineno-1-17" href="#__codelineno-1-17"></a><span class="w"> </span><span class="nx">moderatedBy</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-1-18"><a id="__codelineno-1-18" name="__codelineno-1-18" href="#__codelineno-1-18"></a><span class="w"> </span><span class="nx">select</span><span class="o">:</span><span class="w"> </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">true</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">true</span><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-1-19"><a id="__codelineno-1-19" name="__codelineno-1-19" href="#__codelineno-1-19"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-1-20"><a id="__codelineno-1-20" name="__codelineno-1-20" href="#__codelineno-1-20"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-1-21"><a id="__codelineno-1-21" name="__codelineno-1-21" href="#__codelineno-1-21"></a><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-1-22"><a id="__codelineno-1-22" name="__codelineno-1-22" href="#__codelineno-1-22"></a>
|
||
</span><span id="__span-1-23"><a id="__codelineno-1-23" name="__codelineno-1-23" href="#__codelineno-1-23"></a><span class="w"> </span><span class="c1">// Send notification email if campaign has notifyOnResponse enabled</span>
|
||
</span><span id="__span-1-24"><a id="__codelineno-1-24" name="__codelineno-1-24" href="#__codelineno-1-24"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">status</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">'APPROVED'</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nx">response</span><span class="p">.</span><span class="nx">campaign</span><span class="p">.</span><span class="nx">notifyOnResponse</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-1-25"><a id="__codelineno-1-25" name="__codelineno-1-25" href="#__codelineno-1-25"></a><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">emailService</span><span class="p">.</span><span class="nx">send</span><span class="p">({</span>
|
||
</span><span id="__span-1-26"><a id="__codelineno-1-26" name="__codelineno-1-26" href="#__codelineno-1-26"></a><span class="w"> </span><span class="nx">to</span><span class="o">:</span><span class="w"> </span><span class="kt">response.email</span><span class="p">,</span>
|
||
</span><span id="__span-1-27"><a id="__codelineno-1-27" name="__codelineno-1-27" href="#__codelineno-1-27"></a><span class="w"> </span><span class="nx">subject</span><span class="o">:</span><span class="w"> </span><span class="sb">`Your response was approved`</span><span class="p">,</span>
|
||
</span><span id="__span-1-28"><a id="__codelineno-1-28" name="__codelineno-1-28" href="#__codelineno-1-28"></a><span class="w"> </span><span class="nx">template</span><span class="o">:</span><span class="w"> </span><span class="s1">'response-approved'</span><span class="p">,</span>
|
||
</span><span id="__span-1-29"><a id="__codelineno-1-29" name="__codelineno-1-29" href="#__codelineno-1-29"></a><span class="w"> </span><span class="nx">variables</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-1-30"><a id="__codelineno-1-30" name="__codelineno-1-30" href="#__codelineno-1-30"></a><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="kt">response.name</span><span class="p">,</span>
|
||
</span><span id="__span-1-31"><a id="__codelineno-1-31" name="__codelineno-1-31" href="#__codelineno-1-31"></a><span class="w"> </span><span class="nx">campaignTitle</span><span class="o">:</span><span class="w"> </span><span class="kt">response.campaign.title</span><span class="p">,</span>
|
||
</span><span id="__span-1-32"><a id="__codelineno-1-32" name="__codelineno-1-32" href="#__codelineno-1-32"></a><span class="w"> </span><span class="nx">responseWallUrl</span><span class="o">:</span><span class="w"> </span><span class="sb">`</span><span class="si">${</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">FRONTEND_URL</span><span class="si">}</span><span class="sb">/responses/</span><span class="si">${</span><span class="nx">response</span><span class="p">.</span><span class="nx">campaignId</span><span class="si">}</span><span class="sb">`</span>
|
||
</span><span id="__span-1-33"><a id="__codelineno-1-33" name="__codelineno-1-33" href="#__codelineno-1-33"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-1-34"><a id="__codelineno-1-34" name="__codelineno-1-34" href="#__codelineno-1-34"></a><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-1-35"><a id="__codelineno-1-35" name="__codelineno-1-35" href="#__codelineno-1-35"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-1-36"><a id="__codelineno-1-36" name="__codelineno-1-36" href="#__codelineno-1-36"></a>
|
||
</span><span id="__span-1-37"><a id="__codelineno-1-37" name="__codelineno-1-37" href="#__codelineno-1-37"></a><span class="w"> </span><span class="nx">logger</span><span class="p">.</span><span class="nx">info</span><span class="p">(</span><span class="sb">`Response </span><span class="si">${</span><span class="nx">responseId</span><span class="si">}</span><span class="sb"> </span><span class="si">${</span><span class="nx">status</span><span class="si">}</span><span class="sb"> by user </span><span class="si">${</span><span class="nx">moderatorUserId</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
|
||
</span><span id="__span-1-38"><a id="__codelineno-1-38" name="__codelineno-1-38" href="#__codelineno-1-38"></a>
|
||
</span><span id="__span-1-39"><a id="__codelineno-1-39" name="__codelineno-1-39" href="#__codelineno-1-39"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">response</span><span class="p">;</span>
|
||
</span><span id="__span-1-40"><a id="__codelineno-1-40" name="__codelineno-1-40" href="#__codelineno-1-40"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<h3 id="4-bulk-moderation-actions">4. Bulk Moderation Actions<a class="headerlink" href="#4-bulk-moderation-actions" title="Permanent link">¶</a></h3>
|
||
<p>[Screenshot: ResponsesPage with bulk action toolbar]</p>
|
||
<p><strong>Steps:</strong></p>
|
||
<ol>
|
||
<li>Select multiple responses (checkboxes)</li>
|
||
<li>Click <strong>Bulk Actions</strong> dropdown</li>
|
||
<li>Choose action:</li>
|
||
<li>Approve selected</li>
|
||
<li>Reject selected</li>
|
||
<li>Delete selected</li>
|
||
<li>Confirm bulk action</li>
|
||
<li>All selected responses updated</li>
|
||
</ol>
|
||
<p><strong>Code Example (ResponsesPage.tsx):</strong></p>
|
||
<div class="language-typescript 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="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">selectedRowKeys</span><span class="p">,</span><span class="w"> </span><span class="nx">setSelectedRowKeys</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="kt">string</span><span class="p">[]</span><span class="o">></span><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><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a><span class="kd">const</span><span class="w"> </span><span class="nx">handleBulkApprove</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-2-4"><a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a><span class="w"> </span><span class="k">try</span><span class="w"> </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="k">await</span><span class="w"> </span><span class="nb">Promise</span><span class="p">.</span><span class="nx">all</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="nx">selectedRowKeys</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">id</span><span class="w"> </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="nx">api</span><span class="p">.</span><span class="nx">patch</span><span class="p">(</span><span class="sb">`/responses/</span><span class="si">${</span><span class="nx">id</span><span class="si">}</span><span class="sb">/moderate`</span><span class="p">,</span><span class="w"> </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="nx">status</span><span class="o">:</span><span class="w"> </span><span class="s1">'APPROVED'</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="nx">notes</span><span class="o">:</span><span class="w"> </span><span class="s1">'Bulk approved'</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="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="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="p">);</span>
|
||
</span><span id="__span-2-13"><a id="__codelineno-2-13" name="__codelineno-2-13" href="#__codelineno-2-13"></a>
|
||
</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="nx">message</span><span class="p">.</span><span class="nx">success</span><span class="p">(</span><span class="sb">`Approved </span><span class="si">${</span><span class="nx">selectedRowKeys</span><span class="p">.</span><span class="nx">length</span><span class="si">}</span><span class="sb"> responses`</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="nx">setSelectedRowKeys</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="nx">fetchResponses</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="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-2-18"><a id="__codelineno-2-18" name="__codelineno-2-18" href="#__codelineno-2-18"></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 bulk approve responses'</span><span class="p">);</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="p">};</span>
|
||
</span><span id="__span-2-21"><a id="__codelineno-2-21" name="__codelineno-2-21" href="#__codelineno-2-21"></a>
|
||
</span><span id="__span-2-22"><a id="__codelineno-2-22" name="__codelineno-2-22" href="#__codelineno-2-22"></a><span class="kd">const</span><span class="w"> </span><span class="nx">rowSelection</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="nx">selectedRowKeys</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="nx">onChange</span><span class="o">:</span><span class="w"> </span><span class="kt">setSelectedRowKeys</span>
|
||
</span><span id="__span-2-25"><a id="__codelineno-2-25" name="__codelineno-2-25" href="#__codelineno-2-25"></a><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><span id="__span-2-27"><a id="__codelineno-2-27" name="__codelineno-2-27" href="#__codelineno-2-27"></a><span class="k">return</span><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="o"><></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="p">{</span><span class="nx">selectedRowKeys</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="mf">0</span><span class="w"> </span><span class="o">&&</span><span class="w"> </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="o"><</span><span class="nx">Space</span><span class="w"> </span><span class="nx">style</span><span class="o">=</span><span class="p">{{</span><span class="w"> </span><span class="nx">marginBottom</span><span class="o">:</span><span class="w"> </span><span class="kt">16</span><span class="w"> </span><span class="p">}}</span><span class="o">></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="o"><</span><span class="nx">Button</span><span class="w"> </span><span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">handleBulkApprove</span><span class="p">}</span><span class="o">></span><span class="nx">Approve</span><span class="w"> </span><span class="nx">Selected</span><span class="o"><</span><span class="err">/Button></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="o"><</span><span class="nx">Button</span><span class="w"> </span><span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">handleBulkReject</span><span class="p">}</span><span class="o">></span><span class="nx">Reject</span><span class="w"> </span><span class="nx">Selected</span><span class="o"><</span><span class="err">/Button></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="o"><</span><span class="nx">Button</span><span class="w"> </span><span class="nx">danger</span><span class="w"> </span><span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">handleBulkDelete</span><span class="p">}</span><span class="o">></span><span class="nx">Delete</span><span class="w"> </span><span class="nx">Selected</span><span class="o"><</span><span class="err">/Button></span>
|
||
</span><span id="__span-2-34"><a id="__codelineno-2-34" name="__codelineno-2-34" href="#__codelineno-2-34"></a><span class="w"> </span><span class="o"><</span><span class="err">/Space></span>
|
||
</span><span id="__span-2-35"><a id="__codelineno-2-35" name="__codelineno-2-35" href="#__codelineno-2-35"></a><span class="w"> </span><span class="p">)}</span>
|
||
</span><span id="__span-2-36"><a id="__codelineno-2-36" name="__codelineno-2-36" href="#__codelineno-2-36"></a>
|
||
</span><span id="__span-2-37"><a id="__codelineno-2-37" name="__codelineno-2-37" href="#__codelineno-2-37"></a><span class="w"> </span><span class="o"><</span><span class="nx">Table</span><span class="w"> </span><span class="nx">rowSelection</span><span class="o">=</span><span class="p">{</span><span class="nx">rowSelection</span><span class="p">}</span><span class="w"> </span><span class="nx">dataSource</span><span class="o">=</span><span class="p">{</span><span class="nx">responses</span><span class="p">}</span><span class="w"> </span><span class="nx">columns</span><span class="o">=</span><span class="p">{</span><span class="nx">columns</span><span class="p">}</span><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-2-38"><a id="__codelineno-2-38" name="__codelineno-2-38" href="#__codelineno-2-38"></a><span class="w"> </span><span class="o"><</span><span class="err">/></span>
|
||
</span><span id="__span-2-39"><a id="__codelineno-2-39" name="__codelineno-2-39" href="#__codelineno-2-39"></a><span class="p">);</span>
|
||
</span></code></pre></div>
|
||
<h2 id="public-workflow">Public Workflow<a class="headerlink" href="#public-workflow" title="Permanent link">¶</a></h2>
|
||
<h3 id="1-submit-response">1. Submit Response<a class="headerlink" href="#1-submit-response" title="Permanent link">¶</a></h3>
|
||
<p>[Screenshot: Response submission form on ResponseWallPage]</p>
|
||
<p><strong>User Journey:</strong></p>
|
||
<ol>
|
||
<li>User completes campaign action (sends email)</li>
|
||
<li>Clicks <strong>Share Your Response</strong> link</li>
|
||
<li>Navigated to <code>/responses/{campaignId}/submit</code></li>
|
||
<li>Fills in response form:</li>
|
||
<li>Response type (dropdown)</li>
|
||
<li>Name</li>
|
||
<li>Email</li>
|
||
<li>Postal code (optional)</li>
|
||
<li>Message (what they did)</li>
|
||
<li>Screenshot (optional upload)</li>
|
||
<li>Clicks <strong>Submit Response</strong></li>
|
||
<li>System saves response as PENDING</li>
|
||
<li>Verification email sent (if required)</li>
|
||
</ol>
|
||
<p><strong>Code Example (ResponseWallPage.tsx):</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">handleSubmit</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">values</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 class="w"> </span><span class="p">{</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="k">try</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-3-3"><a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">formData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">FormData</span><span class="p">();</span>
|
||
</span><span id="__span-3-4"><a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a><span class="w"> </span><span class="nx">formData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s1">'campaignId'</span><span class="p">,</span><span class="w"> </span><span class="nx">campaignId</span><span class="p">);</span>
|
||
</span><span id="__span-3-5"><a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a><span class="w"> </span><span class="nx">formData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s1">'responseType'</span><span class="p">,</span><span class="w"> </span><span class="nx">values</span><span class="p">.</span><span class="nx">responseType</span><span class="p">);</span>
|
||
</span><span id="__span-3-6"><a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a><span class="w"> </span><span class="nx">formData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s1">'name'</span><span class="p">,</span><span class="w"> </span><span class="nx">values</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
|
||
</span><span id="__span-3-7"><a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a><span class="w"> </span><span class="nx">formData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s1">'email'</span><span class="p">,</span><span class="w"> </span><span class="nx">values</span><span class="p">.</span><span class="nx">email</span><span class="p">);</span>
|
||
</span><span id="__span-3-8"><a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a><span class="w"> </span><span class="nx">formData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s1">'message'</span><span class="p">,</span><span class="w"> </span><span class="nx">values</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span>
|
||
</span><span id="__span-3-9"><a id="__codelineno-3-9" name="__codelineno-3-9" href="#__codelineno-3-9"></a>
|
||
</span><span id="__span-3-10"><a id="__codelineno-3-10" name="__codelineno-3-10" href="#__codelineno-3-10"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">values</span><span class="p">.</span><span class="nx">postalCode</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-3-11"><a id="__codelineno-3-11" name="__codelineno-3-11" href="#__codelineno-3-11"></a><span class="w"> </span><span class="nx">formData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s1">'postalCode'</span><span class="p">,</span><span class="w"> </span><span class="nx">values</span><span class="p">.</span><span class="nx">postalCode</span><span class="p">);</span>
|
||
</span><span id="__span-3-12"><a id="__codelineno-3-12" name="__codelineno-3-12" href="#__codelineno-3-12"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-3-13"><a id="__codelineno-3-13" name="__codelineno-3-13" href="#__codelineno-3-13"></a>
|
||
</span><span id="__span-3-14"><a id="__codelineno-3-14" name="__codelineno-3-14" href="#__codelineno-3-14"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">values</span><span class="p">.</span><span class="nx">screenshot</span><span class="o">?</span><span class="p">.[</span><span class="mf">0</span><span class="p">]</span><span class="o">?</span><span class="p">.</span><span class="nx">originFileObj</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-3-15"><a id="__codelineno-3-15" name="__codelineno-3-15" href="#__codelineno-3-15"></a><span class="w"> </span><span class="nx">formData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s1">'screenshot'</span><span class="p">,</span><span class="w"> </span><span class="nx">values</span><span class="p">.</span><span class="nx">screenshot</span><span class="p">[</span><span class="mf">0</span><span class="p">].</span><span class="nx">originFileObj</span><span class="p">);</span>
|
||
</span><span id="__span-3-16"><a id="__codelineno-3-16" name="__codelineno-3-16" href="#__codelineno-3-16"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-3-17"><a id="__codelineno-3-17" name="__codelineno-3-17" href="#__codelineno-3-17"></a>
|
||
</span><span id="__span-3-18"><a id="__codelineno-3-18" name="__codelineno-3-18" href="#__codelineno-3-18"></a><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 class="s1">'/api/public/responses'</span><span class="p">,</span><span class="w"> </span><span class="nx">formData</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-3-19"><a id="__codelineno-3-19" name="__codelineno-3-19" href="#__codelineno-3-19"></a><span class="w"> </span><span class="nx">headers</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">'Content-Type'</span><span class="o">:</span><span class="w"> </span><span class="s1">'multipart/form-data'</span><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-3-20"><a id="__codelineno-3-20" name="__codelineno-3-20" href="#__codelineno-3-20"></a><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-3-21"><a id="__codelineno-3-21" name="__codelineno-3-21" href="#__codelineno-3-21"></a>
|
||
</span><span id="__span-3-22"><a id="__codelineno-3-22" name="__codelineno-3-22" href="#__codelineno-3-22"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">campaign</span><span class="p">.</span><span class="nx">requireEmailVerification</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-3-23"><a id="__codelineno-3-23" name="__codelineno-3-23" href="#__codelineno-3-23"></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">'Response submitted! Please check your email to verify.'</span><span class="p">);</span>
|
||
</span><span id="__span-3-24"><a id="__codelineno-3-24" name="__codelineno-3-24" href="#__codelineno-3-24"></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-3-25"><a id="__codelineno-3-25" name="__codelineno-3-25" href="#__codelineno-3-25"></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">'Response submitted! It will appear after admin approval.'</span><span class="p">);</span>
|
||
</span><span id="__span-3-26"><a id="__codelineno-3-26" name="__codelineno-3-26" href="#__codelineno-3-26"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-3-27"><a id="__codelineno-3-27" name="__codelineno-3-27" href="#__codelineno-3-27"></a>
|
||
</span><span id="__span-3-28"><a id="__codelineno-3-28" name="__codelineno-3-28" href="#__codelineno-3-28"></a><span class="w"> </span><span class="nx">form</span><span class="p">.</span><span class="nx">resetFields</span><span class="p">();</span>
|
||
</span><span id="__span-3-29"><a id="__codelineno-3-29" name="__codelineno-3-29" href="#__codelineno-3-29"></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-3-30"><a id="__codelineno-3-30" name="__codelineno-3-30" href="#__codelineno-3-30"></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 submit response'</span><span class="p">);</span>
|
||
</span><span id="__span-3-31"><a id="__codelineno-3-31" name="__codelineno-3-31" href="#__codelineno-3-31"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-3-32"><a id="__codelineno-3-32" name="__codelineno-3-32" href="#__codelineno-3-32"></a><span class="p">};</span>
|
||
</span><span id="__span-3-33"><a id="__codelineno-3-33" name="__codelineno-3-33" href="#__codelineno-3-33"></a>
|
||
</span><span id="__span-3-34"><a id="__codelineno-3-34" name="__codelineno-3-34" href="#__codelineno-3-34"></a><span class="k">return</span><span class="w"> </span><span class="p">(</span>
|
||
</span><span id="__span-3-35"><a id="__codelineno-3-35" name="__codelineno-3-35" href="#__codelineno-3-35"></a><span class="w"> </span><span class="o"><</span><span class="nx">Form</span><span class="w"> </span><span class="nx">form</span><span class="o">=</span><span class="p">{</span><span class="nx">form</span><span class="p">}</span><span class="w"> </span><span class="nx">onFinish</span><span class="o">=</span><span class="p">{</span><span class="nx">handleSubmit</span><span class="p">}</span><span class="w"> </span><span class="nx">layout</span><span class="o">=</span><span class="s2">"vertical"</span><span class="o">></span>
|
||
</span><span id="__span-3-36"><a id="__codelineno-3-36" name="__codelineno-3-36" href="#__codelineno-3-36"></a><span class="w"> </span><span class="o"><</span><span class="nx">Form</span><span class="p">.</span><span class="nx">Item</span>
|
||
</span><span id="__span-3-37"><a id="__codelineno-3-37" name="__codelineno-3-37" href="#__codelineno-3-37"></a><span class="w"> </span><span class="nx">name</span><span class="o">=</span><span class="s2">"responseType"</span>
|
||
</span><span id="__span-3-38"><a id="__codelineno-3-38" name="__codelineno-3-38" href="#__codelineno-3-38"></a><span class="w"> </span><span class="nx">label</span><span class="o">=</span><span class="s2">"What did you do?"</span>
|
||
</span><span id="__span-3-39"><a id="__codelineno-3-39" name="__codelineno-3-39" href="#__codelineno-3-39"></a><span class="w"> </span><span class="nx">rules</span><span class="o">=</span><span class="p">{[{</span><span class="w"> </span><span class="nx">required</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-3-40"><a id="__codelineno-3-40" name="__codelineno-3-40" href="#__codelineno-3-40"></a><span class="w"> </span><span class="o">></span>
|
||
</span><span id="__span-3-41"><a id="__codelineno-3-41" name="__codelineno-3-41" href="#__codelineno-3-41"></a><span class="w"> </span><span class="o"><</span><span class="nx">Select</span><span class="o">></span>
|
||
</span><span id="__span-3-42"><a id="__codelineno-3-42" name="__codelineno-3-42" href="#__codelineno-3-42"></a><span class="w"> </span><span class="o"><</span><span class="nx">Option</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="s2">"EMAIL"</span><span class="o">></span><span class="nx">Sent</span><span class="w"> </span><span class="nx">Email</span><span class="o"><</span><span class="err">/Option></span>
|
||
</span><span id="__span-3-43"><a id="__codelineno-3-43" name="__codelineno-3-43" href="#__codelineno-3-43"></a><span class="w"> </span><span class="o"><</span><span class="nx">Option</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="s2">"LETTER"</span><span class="o">></span><span class="nx">Sent</span><span class="w"> </span><span class="nx">Letter</span><span class="o"><</span><span class="err">/Option></span>
|
||
</span><span id="__span-3-44"><a id="__codelineno-3-44" name="__codelineno-3-44" href="#__codelineno-3-44"></a><span class="w"> </span><span class="o"><</span><span class="nx">Option</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="s2">"PHONE_CALL"</span><span class="o">></span><span class="nx">Made</span><span class="w"> </span><span class="nx">Phone</span><span class="w"> </span><span class="nx">Call</span><span class="o"><</span><span class="err">/Option></span>
|
||
</span><span id="__span-3-45"><a id="__codelineno-3-45" name="__codelineno-3-45" href="#__codelineno-3-45"></a><span class="w"> </span><span class="o"><</span><span class="nx">Option</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="s2">"MEETING"</span><span class="o">></span><span class="nx">Attended</span><span class="w"> </span><span class="nx">Meeting</span><span class="o"><</span><span class="err">/Option></span>
|
||
</span><span id="__span-3-46"><a id="__codelineno-3-46" name="__codelineno-3-46" href="#__codelineno-3-46"></a><span class="w"> </span><span class="o"><</span><span class="nx">Option</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="s2">"SOCIAL_MEDIA"</span><span class="o">></span><span class="nx">Posted</span><span class="w"> </span><span class="nx">on</span><span class="w"> </span><span class="nx">Social</span><span class="w"> </span><span class="nx">Media</span><span class="o"><</span><span class="err">/Option></span>
|
||
</span><span id="__span-3-47"><a id="__codelineno-3-47" name="__codelineno-3-47" href="#__codelineno-3-47"></a><span class="w"> </span><span class="o"><</span><span class="nx">Option</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="s2">"OTHER"</span><span class="o">></span><span class="nx">Other</span><span class="o"><</span><span class="err">/Option></span>
|
||
</span><span id="__span-3-48"><a id="__codelineno-3-48" name="__codelineno-3-48" href="#__codelineno-3-48"></a><span class="w"> </span><span class="o"><</span><span class="err">/Select></span>
|
||
</span><span id="__span-3-49"><a id="__codelineno-3-49" name="__codelineno-3-49" href="#__codelineno-3-49"></a><span class="w"> </span><span class="o"><</span><span class="err">/Form.Item></span>
|
||
</span><span id="__span-3-50"><a id="__codelineno-3-50" name="__codelineno-3-50" href="#__codelineno-3-50"></a>
|
||
</span><span id="__span-3-51"><a id="__codelineno-3-51" name="__codelineno-3-51" href="#__codelineno-3-51"></a><span class="w"> </span><span class="o"><</span><span class="nx">Form</span><span class="p">.</span><span class="nx">Item</span>
|
||
</span><span id="__span-3-52"><a id="__codelineno-3-52" name="__codelineno-3-52" href="#__codelineno-3-52"></a><span class="w"> </span><span class="nx">name</span><span class="o">=</span><span class="s2">"name"</span>
|
||
</span><span id="__span-3-53"><a id="__codelineno-3-53" name="__codelineno-3-53" href="#__codelineno-3-53"></a><span class="w"> </span><span class="nx">label</span><span class="o">=</span><span class="s2">"Your Name"</span>
|
||
</span><span id="__span-3-54"><a id="__codelineno-3-54" name="__codelineno-3-54" href="#__codelineno-3-54"></a><span class="w"> </span><span class="nx">rules</span><span class="o">=</span><span class="p">{[{</span><span class="w"> </span><span class="nx">required</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-3-55"><a id="__codelineno-3-55" name="__codelineno-3-55" href="#__codelineno-3-55"></a><span class="w"> </span><span class="o">></span>
|
||
</span><span id="__span-3-56"><a id="__codelineno-3-56" name="__codelineno-3-56" href="#__codelineno-3-56"></a><span class="w"> </span><span class="o"><</span><span class="nx">Input</span><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-3-57"><a id="__codelineno-3-57" name="__codelineno-3-57" href="#__codelineno-3-57"></a><span class="w"> </span><span class="o"><</span><span class="err">/Form.Item></span>
|
||
</span><span id="__span-3-58"><a id="__codelineno-3-58" name="__codelineno-3-58" href="#__codelineno-3-58"></a>
|
||
</span><span id="__span-3-59"><a id="__codelineno-3-59" name="__codelineno-3-59" href="#__codelineno-3-59"></a><span class="w"> </span><span class="o"><</span><span class="nx">Form</span><span class="p">.</span><span class="nx">Item</span>
|
||
</span><span id="__span-3-60"><a id="__codelineno-3-60" name="__codelineno-3-60" href="#__codelineno-3-60"></a><span class="w"> </span><span class="nx">name</span><span class="o">=</span><span class="s2">"email"</span>
|
||
</span><span id="__span-3-61"><a id="__codelineno-3-61" name="__codelineno-3-61" href="#__codelineno-3-61"></a><span class="w"> </span><span class="nx">label</span><span class="o">=</span><span class="s2">"Your Email"</span>
|
||
</span><span id="__span-3-62"><a id="__codelineno-3-62" name="__codelineno-3-62" href="#__codelineno-3-62"></a><span class="w"> </span><span class="nx">rules</span><span class="o">=</span><span class="p">{[</span>
|
||
</span><span id="__span-3-63"><a id="__codelineno-3-63" name="__codelineno-3-63" href="#__codelineno-3-63"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">required</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-3-64"><a id="__codelineno-3-64" name="__codelineno-3-64" href="#__codelineno-3-64"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="kr">type</span><span class="o">:</span><span class="w"> </span><span class="s1">'email'</span><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-3-65"><a id="__codelineno-3-65" name="__codelineno-3-65" href="#__codelineno-3-65"></a><span class="w"> </span><span class="p">]}</span>
|
||
</span><span id="__span-3-66"><a id="__codelineno-3-66" name="__codelineno-3-66" href="#__codelineno-3-66"></a><span class="w"> </span><span class="o">></span>
|
||
</span><span id="__span-3-67"><a id="__codelineno-3-67" name="__codelineno-3-67" href="#__codelineno-3-67"></a><span class="w"> </span><span class="o"><</span><span class="nx">Input</span><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-3-68"><a id="__codelineno-3-68" name="__codelineno-3-68" href="#__codelineno-3-68"></a><span class="w"> </span><span class="o"><</span><span class="err">/Form.Item></span>
|
||
</span><span id="__span-3-69"><a id="__codelineno-3-69" name="__codelineno-3-69" href="#__codelineno-3-69"></a>
|
||
</span><span id="__span-3-70"><a id="__codelineno-3-70" name="__codelineno-3-70" href="#__codelineno-3-70"></a><span class="w"> </span><span class="o"><</span><span class="nx">Form</span><span class="p">.</span><span class="nx">Item</span>
|
||
</span><span id="__span-3-71"><a id="__codelineno-3-71" name="__codelineno-3-71" href="#__codelineno-3-71"></a><span class="w"> </span><span class="nx">name</span><span class="o">=</span><span class="s2">"message"</span>
|
||
</span><span id="__span-3-72"><a id="__codelineno-3-72" name="__codelineno-3-72" href="#__codelineno-3-72"></a><span class="w"> </span><span class="nx">label</span><span class="o">=</span><span class="s2">"Tell us more"</span>
|
||
</span><span id="__span-3-73"><a id="__codelineno-3-73" name="__codelineno-3-73" href="#__codelineno-3-73"></a><span class="w"> </span><span class="nx">rules</span><span class="o">=</span><span class="p">{[{</span><span class="w"> </span><span class="nx">required</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-3-74"><a id="__codelineno-3-74" name="__codelineno-3-74" href="#__codelineno-3-74"></a><span class="w"> </span><span class="o">></span>
|
||
</span><span id="__span-3-75"><a id="__codelineno-3-75" name="__codelineno-3-75" href="#__codelineno-3-75"></a><span class="w"> </span><span class="o"><</span><span class="nx">Input</span><span class="p">.</span><span class="nx">TextArea</span><span class="w"> </span><span class="nx">rows</span><span class="o">=</span><span class="p">{</span><span class="mf">4</span><span class="p">}</span><span class="w"> </span><span class="nx">placeholder</span><span class="o">=</span><span class="s2">"Describe what you did..."</span><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-3-76"><a id="__codelineno-3-76" name="__codelineno-3-76" href="#__codelineno-3-76"></a><span class="w"> </span><span class="o"><</span><span class="err">/Form.Item></span>
|
||
</span><span id="__span-3-77"><a id="__codelineno-3-77" name="__codelineno-3-77" href="#__codelineno-3-77"></a>
|
||
</span><span id="__span-3-78"><a id="__codelineno-3-78" name="__codelineno-3-78" href="#__codelineno-3-78"></a><span class="w"> </span><span class="o"><</span><span class="nx">Form</span><span class="p">.</span><span class="nx">Item</span>
|
||
</span><span id="__span-3-79"><a id="__codelineno-3-79" name="__codelineno-3-79" href="#__codelineno-3-79"></a><span class="w"> </span><span class="nx">name</span><span class="o">=</span><span class="s2">"screenshot"</span>
|
||
</span><span id="__span-3-80"><a id="__codelineno-3-80" name="__codelineno-3-80" href="#__codelineno-3-80"></a><span class="w"> </span><span class="nx">label</span><span class="o">=</span><span class="s2">"Upload Screenshot (optional)"</span>
|
||
</span><span id="__span-3-81"><a id="__codelineno-3-81" name="__codelineno-3-81" href="#__codelineno-3-81"></a><span class="w"> </span><span class="nx">valuePropName</span><span class="o">=</span><span class="s2">"fileList"</span>
|
||
</span><span id="__span-3-82"><a id="__codelineno-3-82" name="__codelineno-3-82" href="#__codelineno-3-82"></a><span class="w"> </span><span class="nx">getValueFromEvent</span><span class="o">=</span><span class="p">{</span><span class="nx">e</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="nx">e</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="kt">e?.fileList</span><span class="p">}</span>
|
||
</span><span id="__span-3-83"><a id="__codelineno-3-83" name="__codelineno-3-83" href="#__codelineno-3-83"></a><span class="w"> </span><span class="o">></span>
|
||
</span><span id="__span-3-84"><a id="__codelineno-3-84" name="__codelineno-3-84" href="#__codelineno-3-84"></a><span class="w"> </span><span class="o"><</span><span class="nx">Upload</span>
|
||
</span><span id="__span-3-85"><a id="__codelineno-3-85" name="__codelineno-3-85" href="#__codelineno-3-85"></a><span class="w"> </span><span class="nx">listType</span><span class="o">=</span><span class="s2">"picture"</span>
|
||
</span><span id="__span-3-86"><a id="__codelineno-3-86" name="__codelineno-3-86" href="#__codelineno-3-86"></a><span class="w"> </span><span class="nx">maxCount</span><span class="o">=</span><span class="p">{</span><span class="mf">1</span><span class="p">}</span>
|
||
</span><span id="__span-3-87"><a id="__codelineno-3-87" name="__codelineno-3-87" href="#__codelineno-3-87"></a><span class="w"> </span><span class="nx">beforeUpload</span><span class="o">=</span><span class="p">{()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="kc">false</span><span class="p">}</span>
|
||
</span><span id="__span-3-88"><a id="__codelineno-3-88" name="__codelineno-3-88" href="#__codelineno-3-88"></a><span class="w"> </span><span class="o">></span>
|
||
</span><span id="__span-3-89"><a id="__codelineno-3-89" name="__codelineno-3-89" href="#__codelineno-3-89"></a><span class="w"> </span><span class="o"><</span><span class="nx">Button</span><span class="w"> </span><span class="nx">icon</span><span class="o">=</span><span class="p">{</span><span class="o"><</span><span class="nx">UploadOutlined</span><span class="w"> </span><span class="o">/></span><span class="p">}</span><span class="o">></span><span class="nx">Upload</span><span class="w"> </span><span class="nx">Screenshot</span><span class="o"><</span><span class="err">/Button></span>
|
||
</span><span id="__span-3-90"><a id="__codelineno-3-90" name="__codelineno-3-90" href="#__codelineno-3-90"></a><span class="w"> </span><span class="o"><</span><span class="err">/Upload></span>
|
||
</span><span id="__span-3-91"><a id="__codelineno-3-91" name="__codelineno-3-91" href="#__codelineno-3-91"></a><span class="w"> </span><span class="o"><</span><span class="err">/Form.Item></span>
|
||
</span><span id="__span-3-92"><a id="__codelineno-3-92" name="__codelineno-3-92" href="#__codelineno-3-92"></a>
|
||
</span><span id="__span-3-93"><a id="__codelineno-3-93" name="__codelineno-3-93" href="#__codelineno-3-93"></a><span class="w"> </span><span class="o"><</span><span class="nx">Form</span><span class="p">.</span><span class="nx">Item</span><span class="o">></span>
|
||
</span><span id="__span-3-94"><a id="__codelineno-3-94" name="__codelineno-3-94" href="#__codelineno-3-94"></a><span class="w"> </span><span class="o"><</span><span class="nx">Button</span><span class="w"> </span><span class="kr">type</span><span class="o">=</span><span class="s2">"primary"</span><span class="w"> </span><span class="nx">htmlType</span><span class="o">=</span><span class="s2">"submit"</span><span class="o">></span>
|
||
</span><span id="__span-3-95"><a id="__codelineno-3-95" name="__codelineno-3-95" href="#__codelineno-3-95"></a><span class="w"> </span><span class="nx">Submit</span><span class="w"> </span><span class="nx">Response</span>
|
||
</span><span id="__span-3-96"><a id="__codelineno-3-96" name="__codelineno-3-96" href="#__codelineno-3-96"></a><span class="w"> </span><span class="o"><</span><span class="err">/Button></span>
|
||
</span><span id="__span-3-97"><a id="__codelineno-3-97" name="__codelineno-3-97" href="#__codelineno-3-97"></a><span class="w"> </span><span class="o"><</span><span class="err">/Form.Item></span>
|
||
</span><span id="__span-3-98"><a id="__codelineno-3-98" name="__codelineno-3-98" href="#__codelineno-3-98"></a><span class="w"> </span><span class="o"><</span><span class="err">/Form></span>
|
||
</span><span id="__span-3-99"><a id="__codelineno-3-99" name="__codelineno-3-99" href="#__codelineno-3-99"></a><span class="p">);</span>
|
||
</span></code></pre></div>
|
||
<h3 id="2-verify-email">2. Verify Email<a class="headerlink" href="#2-verify-email" title="Permanent link">¶</a></h3>
|
||
<p>[Screenshot: Email verification success page]</p>
|
||
<p><strong>User Journey:</strong></p>
|
||
<ol>
|
||
<li>User receives verification email</li>
|
||
<li>Clicks verification link</li>
|
||
<li>Navigated to <code>/api/public/responses/verify/{token}</code></li>
|
||
<li>System verifies email</li>
|
||
<li>Response marked as email verified</li>
|
||
<li>User redirected to response wall</li>
|
||
<li>Message: "Email verified! Your response will appear after admin approval."</li>
|
||
</ol>
|
||
<p><strong>Verification Email Template:</strong></p>
|
||
<div class="language-html 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 class="nt">h1</span><span class="p">></span>Verify Your Response<span class="p"></</span><span class="nt">h1</span><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><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="p"><</span><span class="nt">p</span><span class="p">></span>Hi {{name}},<span class="p"></</span><span class="nt">p</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><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a><span class="p"><</span><span class="nt">p</span><span class="p">></span>Thanks for sharing your response to <span class="p"><</span><span class="nt">strong</span><span class="p">></span>{{campaignTitle}}<span class="p"></</span><span class="nt">strong</span><span class="p">></span>!<span class="p"></</span><span class="nt">p</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><span id="__span-4-7"><a id="__codelineno-4-7" name="__codelineno-4-7" href="#__codelineno-4-7"></a><span class="p"><</span><span class="nt">p</span><span class="p">></span>Please verify your email address by clicking the link below:<span class="p"></</span><span class="nt">p</span><span class="p">></span>
|
||
</span><span id="__span-4-8"><a id="__codelineno-4-8" name="__codelineno-4-8" href="#__codelineno-4-8"></a>
|
||
</span><span id="__span-4-9"><a id="__codelineno-4-9" name="__codelineno-4-9" href="#__codelineno-4-9"></a><span class="p"><</span><span class="nt">p</span><span class="p">></span>
|
||
</span><span id="__span-4-10"><a id="__codelineno-4-10" name="__codelineno-4-10" href="#__codelineno-4-10"></a> <span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"{{verificationUrl}}"</span><span class="p">></span>Verify Email<span class="p"></</span><span class="nt">a</span><span class="p">></span>
|
||
</span><span id="__span-4-11"><a id="__codelineno-4-11" name="__codelineno-4-11" href="#__codelineno-4-11"></a><span class="p"></</span><span class="nt">p</span><span class="p">></span>
|
||
</span><span id="__span-4-12"><a id="__codelineno-4-12" name="__codelineno-4-12" href="#__codelineno-4-12"></a>
|
||
</span><span id="__span-4-13"><a id="__codelineno-4-13" name="__codelineno-4-13" href="#__codelineno-4-13"></a><span class="p"><</span><span class="nt">p</span><span class="p">></span>This link will expire in 24 hours.<span class="p"></</span><span class="nt">p</span><span class="p">></span>
|
||
</span><span id="__span-4-14"><a id="__codelineno-4-14" name="__codelineno-4-14" href="#__codelineno-4-14"></a>
|
||
</span><span id="__span-4-15"><a id="__codelineno-4-15" name="__codelineno-4-15" href="#__codelineno-4-15"></a><span class="p"><</span><span class="nt">p</span><span class="p">></span>If you didn't submit this response, you can safely ignore this email.<span class="p"></</span><span class="nt">p</span><span class="p">></span>
|
||
</span></code></pre></div>
|
||
<h3 id="3-view-response-wall">3. View Response Wall<a class="headerlink" href="#3-view-response-wall" title="Permanent link">¶</a></h3>
|
||
<p>[Screenshot: Public response wall with approved responses]</p>
|
||
<p><strong>User Journey:</strong></p>
|
||
<ol>
|
||
<li>User visits <code>/responses/{campaignId}</code></li>
|
||
<li>Sees approved responses</li>
|
||
<li>Responses sorted by upvotes (most upvoted first)</li>
|
||
<li>Can upvote responses</li>
|
||
<li>Can filter by response type</li>
|
||
</ol>
|
||
<p><strong>Code Example (ResponseWallPage.tsx):</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">responses</span><span class="p">,</span><span class="w"> </span><span class="nx">setResponses</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">Response</span><span class="p">[]</span><span class="o">></span><span class="p">([]);</span>
|
||
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">filter</span><span class="p">,</span><span class="w"> </span><span class="nx">setFilter</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="kt">string</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><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a>
|
||
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a><span class="nx">useEffect</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-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">fetchResponses</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-5-6"><a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">params</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">URLSearchParams</span><span class="p">();</span>
|
||
</span><span id="__span-5-7"><a id="__codelineno-5-7" name="__codelineno-5-7" href="#__codelineno-5-7"></a>
|
||
</span><span id="__span-5-8"><a id="__codelineno-5-8" name="__codelineno-5-8" href="#__codelineno-5-8"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">filter</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-5-9"><a id="__codelineno-5-9" name="__codelineno-5-9" href="#__codelineno-5-9"></a><span class="w"> </span><span class="nx">params</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s1">'responseType'</span><span class="p">,</span><span class="w"> </span><span class="nx">filter</span><span class="p">);</span>
|
||
</span><span id="__span-5-10"><a id="__codelineno-5-10" name="__codelineno-5-10" href="#__codelineno-5-10"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-5-11"><a id="__codelineno-5-11" name="__codelineno-5-11" href="#__codelineno-5-11"></a>
|
||
</span><span id="__span-5-12"><a id="__codelineno-5-12" name="__codelineno-5-12" href="#__codelineno-5-12"></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">get</span><span class="p">(</span>
|
||
</span><span id="__span-5-13"><a id="__codelineno-5-13" name="__codelineno-5-13" href="#__codelineno-5-13"></a><span class="w"> </span><span class="sb">`/api/public/responses/</span><span class="si">${</span><span class="nx">campaignId</span><span class="si">}</span><span class="sb">?</span><span class="si">${</span><span class="nx">params</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span><span class="si">}</span><span class="sb">`</span>
|
||
</span><span id="__span-5-14"><a id="__codelineno-5-14" name="__codelineno-5-14" href="#__codelineno-5-14"></a><span class="w"> </span><span class="p">);</span>
|
||
</span><span id="__span-5-15"><a id="__codelineno-5-15" name="__codelineno-5-15" href="#__codelineno-5-15"></a>
|
||
</span><span id="__span-5-16"><a id="__codelineno-5-16" name="__codelineno-5-16" href="#__codelineno-5-16"></a><span class="w"> </span><span class="nx">setResponses</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
|
||
</span><span id="__span-5-17"><a id="__codelineno-5-17" name="__codelineno-5-17" href="#__codelineno-5-17"></a><span class="w"> </span><span class="p">};</span>
|
||
</span><span id="__span-5-18"><a id="__codelineno-5-18" name="__codelineno-5-18" href="#__codelineno-5-18"></a>
|
||
</span><span id="__span-5-19"><a id="__codelineno-5-19" name="__codelineno-5-19" href="#__codelineno-5-19"></a><span class="w"> </span><span class="nx">fetchResponses</span><span class="p">();</span>
|
||
</span><span id="__span-5-20"><a id="__codelineno-5-20" name="__codelineno-5-20" href="#__codelineno-5-20"></a><span class="p">},</span><span class="w"> </span><span class="p">[</span><span class="nx">campaignId</span><span class="p">,</span><span class="w"> </span><span class="nx">filter</span><span class="p">]);</span>
|
||
</span><span id="__span-5-21"><a id="__codelineno-5-21" name="__codelineno-5-21" href="#__codelineno-5-21"></a>
|
||
</span><span id="__span-5-22"><a id="__codelineno-5-22" name="__codelineno-5-22" href="#__codelineno-5-22"></a><span class="k">return</span><span class="w"> </span><span class="p">(</span>
|
||
</span><span id="__span-5-23"><a id="__codelineno-5-23" name="__codelineno-5-23" href="#__codelineno-5-23"></a><span class="w"> </span><span class="o"><</span><span class="nx">PublicLayout</span><span class="o">></span>
|
||
</span><span id="__span-5-24"><a id="__codelineno-5-24" name="__codelineno-5-24" href="#__codelineno-5-24"></a><span class="w"> </span><span class="o"><</span><span class="nx">Space</span><span class="w"> </span><span class="nx">direction</span><span class="o">=</span><span class="s2">"vertical"</span><span class="w"> </span><span class="nx">size</span><span class="o">=</span><span class="s2">"large"</span><span class="w"> </span><span class="nx">style</span><span class="o">=</span><span class="p">{{</span><span class="w"> </span><span class="nx">width</span><span class="o">:</span><span class="w"> </span><span class="s1">'100%'</span><span class="w"> </span><span class="p">}}</span><span class="o">></span>
|
||
</span><span id="__span-5-25"><a id="__codelineno-5-25" name="__codelineno-5-25" href="#__codelineno-5-25"></a><span class="w"> </span><span class="o"><</span><span class="nx">Typography</span><span class="p">.</span><span class="nx">Title</span><span class="w"> </span><span class="nx">level</span><span class="o">=</span><span class="p">{</span><span class="mf">2</span><span class="p">}</span><span class="o">></span><span class="nx">Response</span><span class="w"> </span><span class="nx">Wall</span><span class="o"><</span><span class="err">/Typography.Title></span>
|
||
</span><span id="__span-5-26"><a id="__codelineno-5-26" name="__codelineno-5-26" href="#__codelineno-5-26"></a>
|
||
</span><span id="__span-5-27"><a id="__codelineno-5-27" name="__codelineno-5-27" href="#__codelineno-5-27"></a><span class="w"> </span><span class="o"><</span><span class="nx">Radio</span><span class="p">.</span><span class="nx">Group</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="p">{</span><span class="nx">filter</span><span class="p">}</span><span class="w"> </span><span class="nx">onChange</span><span class="o">=</span><span class="p">{</span><span class="nx">e</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">setFilter</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span><span class="p">)}</span><span class="o">></span>
|
||
</span><span id="__span-5-28"><a id="__codelineno-5-28" name="__codelineno-5-28" href="#__codelineno-5-28"></a><span class="w"> </span><span class="o"><</span><span class="nx">Radio</span><span class="p">.</span><span class="nx">Button</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="p">{</span><span class="kc">null</span><span class="p">}</span><span class="o">></span><span class="nx">All</span><span class="o"><</span><span class="err">/Radio.Button></span>
|
||
</span><span id="__span-5-29"><a id="__codelineno-5-29" name="__codelineno-5-29" href="#__codelineno-5-29"></a><span class="w"> </span><span class="o"><</span><span class="nx">Radio</span><span class="p">.</span><span class="nx">Button</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="s2">"EMAIL"</span><span class="o">></span><span class="nx">Emails</span><span class="o"><</span><span class="err">/Radio.Button></span>
|
||
</span><span id="__span-5-30"><a id="__codelineno-5-30" name="__codelineno-5-30" href="#__codelineno-5-30"></a><span class="w"> </span><span class="o"><</span><span class="nx">Radio</span><span class="p">.</span><span class="nx">Button</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="s2">"LETTER"</span><span class="o">></span><span class="nx">Letters</span><span class="o"><</span><span class="err">/Radio.Button></span>
|
||
</span><span id="__span-5-31"><a id="__codelineno-5-31" name="__codelineno-5-31" href="#__codelineno-5-31"></a><span class="w"> </span><span class="o"><</span><span class="nx">Radio</span><span class="p">.</span><span class="nx">Button</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="s2">"PHONE_CALL"</span><span class="o">></span><span class="nx">Calls</span><span class="o"><</span><span class="err">/Radio.Button></span>
|
||
</span><span id="__span-5-32"><a id="__codelineno-5-32" name="__codelineno-5-32" href="#__codelineno-5-32"></a><span class="w"> </span><span class="o"><</span><span class="nx">Radio</span><span class="p">.</span><span class="nx">Button</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="s2">"MEETING"</span><span class="o">></span><span class="nx">Meetings</span><span class="o"><</span><span class="err">/Radio.Button></span>
|
||
</span><span id="__span-5-33"><a id="__codelineno-5-33" name="__codelineno-5-33" href="#__codelineno-5-33"></a><span class="w"> </span><span class="o"><</span><span class="nx">Radio</span><span class="p">.</span><span class="nx">Button</span><span class="w"> </span><span class="nx">value</span><span class="o">=</span><span class="s2">"SOCIAL_MEDIA"</span><span class="o">></span><span class="nx">Social</span><span class="w"> </span><span class="nx">Media</span><span class="o"><</span><span class="err">/Radio.Button></span>
|
||
</span><span id="__span-5-34"><a id="__codelineno-5-34" name="__codelineno-5-34" href="#__codelineno-5-34"></a><span class="w"> </span><span class="o"><</span><span class="err">/Radio.Group></span>
|
||
</span><span id="__span-5-35"><a id="__codelineno-5-35" name="__codelineno-5-35" href="#__codelineno-5-35"></a>
|
||
</span><span id="__span-5-36"><a id="__codelineno-5-36" name="__codelineno-5-36" href="#__codelineno-5-36"></a><span class="w"> </span><span class="o"><</span><span class="nx">List</span>
|
||
</span><span id="__span-5-37"><a id="__codelineno-5-37" name="__codelineno-5-37" href="#__codelineno-5-37"></a><span class="w"> </span><span class="nx">dataSource</span><span class="o">=</span><span class="p">{</span><span class="nx">responses</span><span class="p">}</span>
|
||
</span><span id="__span-5-38"><a id="__codelineno-5-38" name="__codelineno-5-38" href="#__codelineno-5-38"></a><span class="w"> </span><span class="nx">renderItem</span><span class="o">=</span><span class="p">{</span><span class="nx">response</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">(</span>
|
||
</span><span id="__span-5-39"><a id="__codelineno-5-39" name="__codelineno-5-39" href="#__codelineno-5-39"></a><span class="w"> </span><span class="o"><</span><span class="nx">ResponseCard</span>
|
||
</span><span id="__span-5-40"><a id="__codelineno-5-40" name="__codelineno-5-40" href="#__codelineno-5-40"></a><span class="w"> </span><span class="nx">response</span><span class="o">=</span><span class="p">{</span><span class="nx">response</span><span class="p">}</span>
|
||
</span><span id="__span-5-41"><a id="__codelineno-5-41" name="__codelineno-5-41" href="#__codelineno-5-41"></a><span class="w"> </span><span class="nx">onUpvote</span><span class="o">=</span><span class="p">{</span><span class="nx">handleUpvote</span><span class="p">}</span>
|
||
</span><span id="__span-5-42"><a id="__codelineno-5-42" name="__codelineno-5-42" href="#__codelineno-5-42"></a><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-5-43"><a id="__codelineno-5-43" name="__codelineno-5-43" href="#__codelineno-5-43"></a><span class="w"> </span><span class="p">)}</span>
|
||
</span><span id="__span-5-44"><a id="__codelineno-5-44" name="__codelineno-5-44" href="#__codelineno-5-44"></a><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-5-45"><a id="__codelineno-5-45" name="__codelineno-5-45" href="#__codelineno-5-45"></a><span class="w"> </span><span class="o"><</span><span class="err">/Space></span>
|
||
</span><span id="__span-5-46"><a id="__codelineno-5-46" name="__codelineno-5-46" href="#__codelineno-5-46"></a><span class="w"> </span><span class="o"><</span><span class="err">/PublicLayout></span>
|
||
</span><span id="__span-5-47"><a id="__codelineno-5-47" name="__codelineno-5-47" href="#__codelineno-5-47"></a><span class="p">);</span>
|
||
</span></code></pre></div>
|
||
<h3 id="4-upvote-response">4. Upvote Response<a class="headerlink" href="#4-upvote-response" title="Permanent link">¶</a></h3>
|
||
<p>[Screenshot: Response card with upvote button]</p>
|
||
<p><strong>User Journey:</strong></p>
|
||
<ol>
|
||
<li>User clicks upvote button on response</li>
|
||
<li>System checks for existing upvote (IP + user)</li>
|
||
<li>If first upvote → increment count, save upvote record</li>
|
||
<li>If already upvoted → show message "You already upvoted this"</li>
|
||
<li>Upvote count updated in real-time</li>
|
||
</ol>
|
||
<p><strong>Code Example (responses-public.routes.ts):</strong></p>
|
||
<div class="language-typescript 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="nx">router</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'/:id/upvote'</span><span class="p">,</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="p">(</span><span class="nx">req</span><span class="p">,</span><span class="w"> </span><span class="nx">res</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-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a><span class="w"> </span><span class="k">try</span><span class="w"> </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="kd">const</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="p">}</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">params</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="kd">const</span><span class="w"> </span><span class="nx">ipAddress</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="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="kd">const</span><span class="w"> </span><span class="nx">userId</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">user</span><span class="o">?</span><span class="p">.</span><span class="nx">id</span><span class="p">;</span><span class="w"> </span><span class="c1">// If authenticated</span>
|
||
</span><span id="__span-6-6"><a id="__codelineno-6-6" name="__codelineno-6-6" href="#__codelineno-6-6"></a>
|
||
</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="c1">// Check for existing upvote</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="kd">const</span><span class="w"> </span><span class="nx">existingUpvote</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">responseUpvote</span><span class="p">.</span><span class="nx">findFirst</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="nx">where</span><span class="o">:</span><span class="w"> </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="nx">responseId</span><span class="o">:</span><span class="w"> </span><span class="kt">id</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="nx">OR</span><span class="o">:</span><span class="w"> </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="p">{</span><span class="w"> </span><span class="nx">ipAddress</span><span class="w"> </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="nx">userId</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">userId</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">:</span><span class="w"> </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="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="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="p">});</span>
|
||
</span><span id="__span-6-17"><a id="__codelineno-6-17" name="__codelineno-6-17" href="#__codelineno-6-17"></a>
|
||
</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="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">existingUpvote</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="k">return</span><span class="w"> </span><span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mf">400</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span><span class="w"> </span><span class="nx">error</span><span class="o">:</span><span class="w"> </span><span class="s1">'You already upvoted this response'</span><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="p">}</span>
|
||
</span><span id="__span-6-21"><a id="__codelineno-6-21" name="__codelineno-6-21" href="#__codelineno-6-21"></a>
|
||
</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="c1">// Create upvote and increment count (transaction)</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="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-6-24"><a id="__codelineno-6-24" name="__codelineno-6-24" href="#__codelineno-6-24"></a><span class="w"> </span><span class="nx">prisma</span><span class="p">.</span><span class="nx">responseUpvote</span><span class="p">.</span><span class="nx">create</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="nx">data</span><span class="o">:</span><span class="w"> </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="nx">responseId</span><span class="o">:</span><span class="w"> </span><span class="kt">id</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="nx">ipAddress</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="nx">userId</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="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="nx">prisma</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nx">update</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="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="w"> </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="nx">data</span><span class="o">:</span><span class="w"> </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="nx">upvotes</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><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="p">}</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><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="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span><span class="w"> </span><span class="nx">success</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-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 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-6-41"><a id="__codelineno-6-41" name="__codelineno-6-41" href="#__codelineno-6-41"></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 upvote response:'</span><span class="p">,</span><span class="w"> </span><span class="nx">error</span><span class="p">);</span>
|
||
</span><span id="__span-6-42"><a id="__codelineno-6-42" name="__codelineno-6-42" href="#__codelineno-6-42"></a><span class="w"> </span><span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mf">500</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span><span class="w"> </span><span class="nx">error</span><span class="o">:</span><span class="w"> </span><span class="s1">'Failed to upvote response'</span><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-6-43"><a id="__codelineno-6-43" name="__codelineno-6-43" href="#__codelineno-6-43"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-6-44"><a id="__codelineno-6-44" name="__codelineno-6-44" href="#__codelineno-6-44"></a><span class="p">});</span>
|
||
</span></code></pre></div>
|
||
<h2 id="volunteer-workflow">Volunteer Workflow<a class="headerlink" href="#volunteer-workflow" title="Permanent link">¶</a></h2>
|
||
<p>Not applicable — response wall is public-facing.</p>
|
||
<h2 id="code-examples">Code Examples<a class="headerlink" href="#code-examples" title="Permanent link">¶</a></h2>
|
||
<h3 id="backend-email-verification-token">Backend: Email Verification Token<a class="headerlink" href="#backend-email-verification-token" title="Permanent link">¶</a></h3>
|
||
<div class="language-typescript 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="c1">// api/src/modules/influence/responses/responses.service.ts</span>
|
||
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a>
|
||
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a><span class="k">import</span><span class="w"> </span><span class="nx">crypto</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'crypto'</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><span id="__span-7-5"><a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></a><span class="k">async</span><span class="w"> </span><span class="nx">createResponse</span><span class="p">(</span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="kt">any</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">Response</span><span class="o">></span><span class="w"> </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="kd">const</span><span class="w"> </span><span class="nx">verificationToken</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">crypto</span><span class="p">.</span><span class="nx">randomBytes</span><span class="p">(</span><span class="mf">32</span><span class="p">).</span><span class="nx">toString</span><span class="p">(</span><span class="s1">'hex'</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><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="kd">const</span><span class="w"> </span><span class="nx">response</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">prisma</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nx">create</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="nx">data</span><span class="o">:</span><span class="w"> </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="p">...</span><span class="nx">data</span><span class="p">,</span>
|
||
</span><span id="__span-7-11"><a id="__codelineno-7-11" name="__codelineno-7-11" href="#__codelineno-7-11"></a><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="s1">'PENDING'</span><span class="p">,</span>
|
||
</span><span id="__span-7-12"><a id="__codelineno-7-12" name="__codelineno-7-12" href="#__codelineno-7-12"></a><span class="w"> </span><span class="nx">isEmailVerified</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span>
|
||
</span><span id="__span-7-13"><a id="__codelineno-7-13" name="__codelineno-7-13" href="#__codelineno-7-13"></a><span class="w"> </span><span class="nx">verificationToken</span><span class="p">,</span>
|
||
</span><span id="__span-7-14"><a id="__codelineno-7-14" name="__codelineno-7-14" href="#__codelineno-7-14"></a><span class="w"> </span><span class="nx">verificationTokenExpires</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="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mf">24</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">60</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">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">// 24h</span>
|
||
</span><span id="__span-7-15"><a id="__codelineno-7-15" name="__codelineno-7-15" href="#__codelineno-7-15"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-7-16"><a id="__codelineno-7-16" name="__codelineno-7-16" href="#__codelineno-7-16"></a><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-7-17"><a id="__codelineno-7-17" name="__codelineno-7-17" href="#__codelineno-7-17"></a>
|
||
</span><span id="__span-7-18"><a id="__codelineno-7-18" name="__codelineno-7-18" href="#__codelineno-7-18"></a><span class="w"> </span><span class="c1">// Send verification email</span>
|
||
</span><span id="__span-7-19"><a id="__codelineno-7-19" name="__codelineno-7-19" href="#__codelineno-7-19"></a><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">emailService</span><span class="p">.</span><span class="nx">send</span><span class="p">({</span>
|
||
</span><span id="__span-7-20"><a id="__codelineno-7-20" name="__codelineno-7-20" href="#__codelineno-7-20"></a><span class="w"> </span><span class="nx">to</span><span class="o">:</span><span class="w"> </span><span class="kt">response.email</span><span class="p">,</span>
|
||
</span><span id="__span-7-21"><a id="__codelineno-7-21" name="__codelineno-7-21" href="#__codelineno-7-21"></a><span class="w"> </span><span class="nx">subject</span><span class="o">:</span><span class="w"> </span><span class="s1">'Verify your response'</span><span class="p">,</span>
|
||
</span><span id="__span-7-22"><a id="__codelineno-7-22" name="__codelineno-7-22" href="#__codelineno-7-22"></a><span class="w"> </span><span class="nx">template</span><span class="o">:</span><span class="w"> </span><span class="s1">'response-verification'</span><span class="p">,</span>
|
||
</span><span id="__span-7-23"><a id="__codelineno-7-23" name="__codelineno-7-23" href="#__codelineno-7-23"></a><span class="w"> </span><span class="nx">variables</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-7-24"><a id="__codelineno-7-24" name="__codelineno-7-24" href="#__codelineno-7-24"></a><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="kt">response.name</span><span class="p">,</span>
|
||
</span><span id="__span-7-25"><a id="__codelineno-7-25" name="__codelineno-7-25" href="#__codelineno-7-25"></a><span class="w"> </span><span class="nx">campaignTitle</span><span class="o">:</span><span class="w"> </span><span class="kt">response.campaign.title</span><span class="p">,</span>
|
||
</span><span id="__span-7-26"><a id="__codelineno-7-26" name="__codelineno-7-26" href="#__codelineno-7-26"></a><span class="w"> </span><span class="nx">verificationUrl</span><span class="o">:</span><span class="w"> </span><span class="sb">`</span><span class="si">${</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">RESPONSE_VERIFICATION_URL</span><span class="si">}</span><span class="sb">/api/public/responses/verify/</span><span class="si">${</span><span class="nx">verificationToken</span><span class="si">}</span><span class="sb">`</span>
|
||
</span><span id="__span-7-27"><a id="__codelineno-7-27" name="__codelineno-7-27" href="#__codelineno-7-27"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-7-28"><a id="__codelineno-7-28" name="__codelineno-7-28" href="#__codelineno-7-28"></a><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-7-29"><a id="__codelineno-7-29" name="__codelineno-7-29" href="#__codelineno-7-29"></a>
|
||
</span><span id="__span-7-30"><a id="__codelineno-7-30" name="__codelineno-7-30" href="#__codelineno-7-30"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">response</span><span class="p">;</span>
|
||
</span><span id="__span-7-31"><a id="__codelineno-7-31" name="__codelineno-7-31" href="#__codelineno-7-31"></a><span class="p">}</span>
|
||
</span><span id="__span-7-32"><a id="__codelineno-7-32" name="__codelineno-7-32" href="#__codelineno-7-32"></a>
|
||
</span><span id="__span-7-33"><a id="__codelineno-7-33" name="__codelineno-7-33" href="#__codelineno-7-33"></a><span class="k">async</span><span class="w"> </span><span class="nx">verifyEmail</span><span class="p">(</span><span class="nx">token</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">Response</span><span class="o">></span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-7-34"><a id="__codelineno-7-34" name="__codelineno-7-34" href="#__codelineno-7-34"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">response</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">prisma</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nx">findFirst</span><span class="p">({</span>
|
||
</span><span id="__span-7-35"><a id="__codelineno-7-35" name="__codelineno-7-35" href="#__codelineno-7-35"></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-7-36"><a id="__codelineno-7-36" name="__codelineno-7-36" href="#__codelineno-7-36"></a><span class="w"> </span><span class="nx">verificationToken</span><span class="o">:</span><span class="w"> </span><span class="kt">token</span><span class="p">,</span>
|
||
</span><span id="__span-7-37"><a id="__codelineno-7-37" name="__codelineno-7-37" href="#__codelineno-7-37"></a><span class="w"> </span><span class="nx">verificationTokenExpires</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-7-38"><a id="__codelineno-7-38" name="__codelineno-7-38" href="#__codelineno-7-38"></a><span class="w"> </span><span class="nx">gt</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><span id="__span-7-39"><a id="__codelineno-7-39" name="__codelineno-7-39" href="#__codelineno-7-39"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-7-40"><a id="__codelineno-7-40" name="__codelineno-7-40" href="#__codelineno-7-40"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-7-41"><a id="__codelineno-7-41" name="__codelineno-7-41" href="#__codelineno-7-41"></a><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-7-42"><a id="__codelineno-7-42" name="__codelineno-7-42" href="#__codelineno-7-42"></a>
|
||
</span><span id="__span-7-43"><a id="__codelineno-7-43" name="__codelineno-7-43" href="#__codelineno-7-43"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-7-44"><a id="__codelineno-7-44" name="__codelineno-7-44" href="#__codelineno-7-44"></a><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="s1">'Invalid or expired verification token'</span><span class="p">);</span>
|
||
</span><span id="__span-7-45"><a id="__codelineno-7-45" name="__codelineno-7-45" href="#__codelineno-7-45"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-7-46"><a id="__codelineno-7-46" name="__codelineno-7-46" href="#__codelineno-7-46"></a>
|
||
</span><span id="__span-7-47"><a id="__codelineno-7-47" name="__codelineno-7-47" href="#__codelineno-7-47"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">prisma</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nx">update</span><span class="p">({</span>
|
||
</span><span id="__span-7-48"><a id="__codelineno-7-48" name="__codelineno-7-48" href="#__codelineno-7-48"></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">response.id</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-7-49"><a id="__codelineno-7-49" name="__codelineno-7-49" href="#__codelineno-7-49"></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-7-50"><a id="__codelineno-7-50" name="__codelineno-7-50" href="#__codelineno-7-50"></a><span class="w"> </span><span class="nx">isEmailVerified</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="p">,</span>
|
||
</span><span id="__span-7-51"><a id="__codelineno-7-51" name="__codelineno-7-51" href="#__codelineno-7-51"></a><span class="w"> </span><span class="nx">verificationToken</span><span class="o">:</span><span class="w"> </span><span class="kt">null</span><span class="p">,</span>
|
||
</span><span id="__span-7-52"><a id="__codelineno-7-52" name="__codelineno-7-52" href="#__codelineno-7-52"></a><span class="w"> </span><span class="nx">verificationTokenExpires</span><span class="o">:</span><span class="w"> </span><span class="kt">null</span>
|
||
</span><span id="__span-7-53"><a id="__codelineno-7-53" name="__codelineno-7-53" href="#__codelineno-7-53"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-7-54"><a id="__codelineno-7-54" name="__codelineno-7-54" href="#__codelineno-7-54"></a><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-7-55"><a id="__codelineno-7-55" name="__codelineno-7-55" href="#__codelineno-7-55"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<h3 id="frontend-response-card-component">Frontend: Response Card Component<a class="headerlink" href="#frontend-response-card-component" title="Permanent link">¶</a></h3>
|
||
<div class="language-typescript 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="c1">// admin/src/components/influence/ResponseCard.tsx</span>
|
||
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a>
|
||
</span><span id="__span-8-3"><a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a><span class="k">import</span><span class="w"> </span><span class="nx">React</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'react'</span><span class="p">;</span>
|
||
</span><span id="__span-8-4"><a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">Card</span><span class="p">,</span><span class="w"> </span><span class="nx">Space</span><span class="p">,</span><span class="w"> </span><span class="nx">Typography</span><span class="p">,</span><span class="w"> </span><span class="nx">Tag</span><span class="p">,</span><span class="w"> </span><span class="nx">Button</span><span class="p">,</span><span class="w"> </span><span class="nx">Avatar</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-8-5"><a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">LikeOutlined</span><span class="p">,</span><span class="w"> </span><span class="nx">LikeFilled</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">'@ant-design/icons'</span><span class="p">;</span>
|
||
</span><span id="__span-8-6"><a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a><span class="k">import</span><span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">Response</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">'../../types/api'</span><span class="p">;</span>
|
||
</span><span id="__span-8-7"><a id="__codelineno-8-7" name="__codelineno-8-7" href="#__codelineno-8-7"></a>
|
||
</span><span id="__span-8-8"><a id="__codelineno-8-8" name="__codelineno-8-8" href="#__codelineno-8-8"></a><span class="kd">interface</span><span class="w"> </span><span class="nx">ResponseCardProps</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-8-9"><a id="__codelineno-8-9" name="__codelineno-8-9" href="#__codelineno-8-9"></a><span class="w"> </span><span class="nx">response</span><span class="o">:</span><span class="w"> </span><span class="kt">Response</span><span class="p">;</span>
|
||
</span><span id="__span-8-10"><a id="__codelineno-8-10" name="__codelineno-8-10" href="#__codelineno-8-10"></a><span class="w"> </span><span class="nx">onUpvote</span><span class="o">:</span><span class="w"> </span><span class="p">(</span><span class="nx">id</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="ow">void</span><span class="p">;</span>
|
||
</span><span id="__span-8-11"><a id="__codelineno-8-11" name="__codelineno-8-11" href="#__codelineno-8-11"></a><span class="w"> </span><span class="nx">hasUpvoted?</span><span class="o">:</span><span class="w"> </span><span class="kt">boolean</span><span class="p">;</span>
|
||
</span><span id="__span-8-12"><a id="__codelineno-8-12" name="__codelineno-8-12" href="#__codelineno-8-12"></a><span class="p">}</span>
|
||
</span><span id="__span-8-13"><a id="__codelineno-8-13" name="__codelineno-8-13" href="#__codelineno-8-13"></a>
|
||
</span><span id="__span-8-14"><a id="__codelineno-8-14" name="__codelineno-8-14" href="#__codelineno-8-14"></a><span class="kd">const</span><span class="w"> </span><span class="nx">ResponseCard</span><span class="o">:</span><span class="w"> </span><span class="kt">React.FC</span><span class="o"><</span><span class="nx">ResponseCardProps</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-8-15"><a id="__codelineno-8-15" name="__codelineno-8-15" href="#__codelineno-8-15"></a><span class="w"> </span><span class="nx">response</span><span class="p">,</span>
|
||
</span><span id="__span-8-16"><a id="__codelineno-8-16" name="__codelineno-8-16" href="#__codelineno-8-16"></a><span class="w"> </span><span class="nx">onUpvote</span><span class="p">,</span>
|
||
</span><span id="__span-8-17"><a id="__codelineno-8-17" name="__codelineno-8-17" href="#__codelineno-8-17"></a><span class="w"> </span><span class="nx">hasUpvoted</span>
|
||
</span><span id="__span-8-18"><a id="__codelineno-8-18" name="__codelineno-8-18" href="#__codelineno-8-18"></a><span class="p">})</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-8-19"><a id="__codelineno-8-19" name="__codelineno-8-19" href="#__codelineno-8-19"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">typeColors</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-8-20"><a id="__codelineno-8-20" name="__codelineno-8-20" href="#__codelineno-8-20"></a><span class="w"> </span><span class="nx">EMAIL</span><span class="o">:</span><span class="w"> </span><span class="s1">'blue'</span><span class="p">,</span>
|
||
</span><span id="__span-8-21"><a id="__codelineno-8-21" name="__codelineno-8-21" href="#__codelineno-8-21"></a><span class="w"> </span><span class="nx">LETTER</span><span class="o">:</span><span class="w"> </span><span class="s1">'green'</span><span class="p">,</span>
|
||
</span><span id="__span-8-22"><a id="__codelineno-8-22" name="__codelineno-8-22" href="#__codelineno-8-22"></a><span class="w"> </span><span class="nx">PHONE_CALL</span><span class="o">:</span><span class="w"> </span><span class="s1">'orange'</span><span class="p">,</span>
|
||
</span><span id="__span-8-23"><a id="__codelineno-8-23" name="__codelineno-8-23" href="#__codelineno-8-23"></a><span class="w"> </span><span class="nx">MEETING</span><span class="o">:</span><span class="w"> </span><span class="s1">'purple'</span><span class="p">,</span>
|
||
</span><span id="__span-8-24"><a id="__codelineno-8-24" name="__codelineno-8-24" href="#__codelineno-8-24"></a><span class="w"> </span><span class="nx">SOCIAL_MEDIA</span><span class="o">:</span><span class="w"> </span><span class="s1">'cyan'</span><span class="p">,</span>
|
||
</span><span id="__span-8-25"><a id="__codelineno-8-25" name="__codelineno-8-25" href="#__codelineno-8-25"></a><span class="w"> </span><span class="nx">OTHER</span><span class="o">:</span><span class="w"> </span><span class="s1">'default'</span>
|
||
</span><span id="__span-8-26"><a id="__codelineno-8-26" name="__codelineno-8-26" href="#__codelineno-8-26"></a><span class="w"> </span><span class="p">};</span>
|
||
</span><span id="__span-8-27"><a id="__codelineno-8-27" name="__codelineno-8-27" href="#__codelineno-8-27"></a>
|
||
</span><span id="__span-8-28"><a id="__codelineno-8-28" name="__codelineno-8-28" href="#__codelineno-8-28"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">typeLabels</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-8-29"><a id="__codelineno-8-29" name="__codelineno-8-29" href="#__codelineno-8-29"></a><span class="w"> </span><span class="nx">EMAIL</span><span class="o">:</span><span class="w"> </span><span class="s1">'Sent Email'</span><span class="p">,</span>
|
||
</span><span id="__span-8-30"><a id="__codelineno-8-30" name="__codelineno-8-30" href="#__codelineno-8-30"></a><span class="w"> </span><span class="nx">LETTER</span><span class="o">:</span><span class="w"> </span><span class="s1">'Sent Letter'</span><span class="p">,</span>
|
||
</span><span id="__span-8-31"><a id="__codelineno-8-31" name="__codelineno-8-31" href="#__codelineno-8-31"></a><span class="w"> </span><span class="nx">PHONE_CALL</span><span class="o">:</span><span class="w"> </span><span class="s1">'Made Call'</span><span class="p">,</span>
|
||
</span><span id="__span-8-32"><a id="__codelineno-8-32" name="__codelineno-8-32" href="#__codelineno-8-32"></a><span class="w"> </span><span class="nx">MEETING</span><span class="o">:</span><span class="w"> </span><span class="s1">'Attended Meeting'</span><span class="p">,</span>
|
||
</span><span id="__span-8-33"><a id="__codelineno-8-33" name="__codelineno-8-33" href="#__codelineno-8-33"></a><span class="w"> </span><span class="nx">SOCIAL_MEDIA</span><span class="o">:</span><span class="w"> </span><span class="s1">'Posted on Social Media'</span><span class="p">,</span>
|
||
</span><span id="__span-8-34"><a id="__codelineno-8-34" name="__codelineno-8-34" href="#__codelineno-8-34"></a><span class="w"> </span><span class="nx">OTHER</span><span class="o">:</span><span class="w"> </span><span class="s1">'Other Action'</span>
|
||
</span><span id="__span-8-35"><a id="__codelineno-8-35" name="__codelineno-8-35" href="#__codelineno-8-35"></a><span class="w"> </span><span class="p">};</span>
|
||
</span><span id="__span-8-36"><a id="__codelineno-8-36" name="__codelineno-8-36" href="#__codelineno-8-36"></a>
|
||
</span><span id="__span-8-37"><a id="__codelineno-8-37" name="__codelineno-8-37" href="#__codelineno-8-37"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">(</span>
|
||
</span><span id="__span-8-38"><a id="__codelineno-8-38" name="__codelineno-8-38" href="#__codelineno-8-38"></a><span class="w"> </span><span class="o"><</span><span class="nx">Card</span><span class="o">></span>
|
||
</span><span id="__span-8-39"><a id="__codelineno-8-39" name="__codelineno-8-39" href="#__codelineno-8-39"></a><span class="w"> </span><span class="o"><</span><span class="nx">Space</span><span class="w"> </span><span class="nx">direction</span><span class="o">=</span><span class="s2">"vertical"</span><span class="w"> </span><span class="nx">size</span><span class="o">=</span><span class="s2">"small"</span><span class="w"> </span><span class="nx">style</span><span class="o">=</span><span class="p">{{</span><span class="w"> </span><span class="nx">width</span><span class="o">:</span><span class="w"> </span><span class="s1">'100%'</span><span class="w"> </span><span class="p">}}</span><span class="o">></span>
|
||
</span><span id="__span-8-40"><a id="__codelineno-8-40" name="__codelineno-8-40" href="#__codelineno-8-40"></a><span class="w"> </span><span class="o"><</span><span class="nx">Space</span><span class="o">></span>
|
||
</span><span id="__span-8-41"><a id="__codelineno-8-41" name="__codelineno-8-41" href="#__codelineno-8-41"></a><span class="w"> </span><span class="o"><</span><span class="nx">Avatar</span><span class="o">></span><span class="p">{</span><span class="nx">response</span><span class="p">.</span><span class="nx">name</span><span class="p">[</span><span class="mf">0</span><span class="p">].</span><span class="nx">toUpperCase</span><span class="p">()}</span><span class="o"><</span><span class="err">/Avatar></span>
|
||
</span><span id="__span-8-42"><a id="__codelineno-8-42" name="__codelineno-8-42" href="#__codelineno-8-42"></a><span class="w"> </span><span class="o"><</span><span class="nx">Space</span><span class="w"> </span><span class="nx">direction</span><span class="o">=</span><span class="s2">"vertical"</span><span class="w"> </span><span class="nx">size</span><span class="o">=</span><span class="p">{</span><span class="mf">0</span><span class="p">}</span><span class="o">></span>
|
||
</span><span id="__span-8-43"><a id="__codelineno-8-43" name="__codelineno-8-43" href="#__codelineno-8-43"></a><span class="w"> </span><span class="o"><</span><span class="nx">Typography</span><span class="p">.</span><span class="nx">Text</span><span class="w"> </span><span class="nx">strong</span><span class="o">></span><span class="p">{</span><span class="nx">response</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="o"><</span><span class="err">/Typography.Text></span>
|
||
</span><span id="__span-8-44"><a id="__codelineno-8-44" name="__codelineno-8-44" href="#__codelineno-8-44"></a><span class="w"> </span><span class="o"><</span><span class="nx">Typography</span><span class="p">.</span><span class="nx">Text</span><span class="w"> </span><span class="kr">type</span><span class="o">=</span><span class="s2">"secondary"</span><span class="o">></span>
|
||
</span><span id="__span-8-45"><a id="__codelineno-8-45" name="__codelineno-8-45" href="#__codelineno-8-45"></a><span class="w"> </span><span class="p">{</span><span class="ow">new</span><span class="w"> </span><span class="nb">Date</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">createdAt</span><span class="p">).</span><span class="nx">toLocaleDateString</span><span class="p">()}</span>
|
||
</span><span id="__span-8-46"><a id="__codelineno-8-46" name="__codelineno-8-46" href="#__codelineno-8-46"></a><span class="w"> </span><span class="o"><</span><span class="err">/Typography.Text></span>
|
||
</span><span id="__span-8-47"><a id="__codelineno-8-47" name="__codelineno-8-47" href="#__codelineno-8-47"></a><span class="w"> </span><span class="o"><</span><span class="err">/Space></span>
|
||
</span><span id="__span-8-48"><a id="__codelineno-8-48" name="__codelineno-8-48" href="#__codelineno-8-48"></a><span class="w"> </span><span class="o"><</span><span class="err">/Space></span>
|
||
</span><span id="__span-8-49"><a id="__codelineno-8-49" name="__codelineno-8-49" href="#__codelineno-8-49"></a>
|
||
</span><span id="__span-8-50"><a id="__codelineno-8-50" name="__codelineno-8-50" href="#__codelineno-8-50"></a><span class="w"> </span><span class="o"><</span><span class="nx">Tag</span><span class="w"> </span><span class="nx">color</span><span class="o">=</span><span class="p">{</span><span class="nx">typeColors</span><span class="p">[</span><span class="nx">response</span><span class="p">.</span><span class="nx">responseType</span><span class="p">]}</span><span class="o">></span>
|
||
</span><span id="__span-8-51"><a id="__codelineno-8-51" name="__codelineno-8-51" href="#__codelineno-8-51"></a><span class="w"> </span><span class="p">{</span><span class="nx">typeLabels</span><span class="p">[</span><span class="nx">response</span><span class="p">.</span><span class="nx">responseType</span><span class="p">]}</span>
|
||
</span><span id="__span-8-52"><a id="__codelineno-8-52" name="__codelineno-8-52" href="#__codelineno-8-52"></a><span class="w"> </span><span class="o"><</span><span class="err">/Tag></span>
|
||
</span><span id="__span-8-53"><a id="__codelineno-8-53" name="__codelineno-8-53" href="#__codelineno-8-53"></a>
|
||
</span><span id="__span-8-54"><a id="__codelineno-8-54" name="__codelineno-8-54" href="#__codelineno-8-54"></a><span class="w"> </span><span class="o"><</span><span class="nx">Typography</span><span class="p">.</span><span class="nx">Paragraph</span><span class="o">></span><span class="p">{</span><span class="nx">response</span><span class="p">.</span><span class="nx">message</span><span class="p">}</span><span class="o"><</span><span class="err">/Typography.Paragraph></span>
|
||
</span><span id="__span-8-55"><a id="__codelineno-8-55" name="__codelineno-8-55" href="#__codelineno-8-55"></a>
|
||
</span><span id="__span-8-56"><a id="__codelineno-8-56" name="__codelineno-8-56" href="#__codelineno-8-56"></a><span class="w"> </span><span class="p">{</span><span class="nx">response</span><span class="p">.</span><span class="nx">screenshotUrl</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="p">(</span>
|
||
</span><span id="__span-8-57"><a id="__codelineno-8-57" name="__codelineno-8-57" href="#__codelineno-8-57"></a><span class="w"> </span><span class="o"><</span><span class="nx">img</span>
|
||
</span><span id="__span-8-58"><a id="__codelineno-8-58" name="__codelineno-8-58" href="#__codelineno-8-58"></a><span class="w"> </span><span class="nx">src</span><span class="o">=</span><span class="p">{</span><span class="nx">response</span><span class="p">.</span><span class="nx">screenshotUrl</span><span class="p">}</span>
|
||
</span><span id="__span-8-59"><a id="__codelineno-8-59" name="__codelineno-8-59" href="#__codelineno-8-59"></a><span class="w"> </span><span class="nx">alt</span><span class="o">=</span><span class="s2">"Response screenshot"</span>
|
||
</span><span id="__span-8-60"><a id="__codelineno-8-60" name="__codelineno-8-60" href="#__codelineno-8-60"></a><span class="w"> </span><span class="nx">style</span><span class="o">=</span><span class="p">{{</span><span class="w"> </span><span class="nx">maxWidth</span><span class="o">:</span><span class="w"> </span><span class="s1">'100%'</span><span class="p">,</span><span class="w"> </span><span class="nx">borderRadius</span><span class="o">:</span><span class="w"> </span><span class="kt">4</span><span class="w"> </span><span class="p">}}</span>
|
||
</span><span id="__span-8-61"><a id="__codelineno-8-61" name="__codelineno-8-61" href="#__codelineno-8-61"></a><span class="w"> </span><span class="o">/></span>
|
||
</span><span id="__span-8-62"><a id="__codelineno-8-62" name="__codelineno-8-62" href="#__codelineno-8-62"></a><span class="w"> </span><span class="p">)}</span>
|
||
</span><span id="__span-8-63"><a id="__codelineno-8-63" name="__codelineno-8-63" href="#__codelineno-8-63"></a>
|
||
</span><span id="__span-8-64"><a id="__codelineno-8-64" name="__codelineno-8-64" href="#__codelineno-8-64"></a><span class="w"> </span><span class="o"><</span><span class="nx">Button</span>
|
||
</span><span id="__span-8-65"><a id="__codelineno-8-65" name="__codelineno-8-65" href="#__codelineno-8-65"></a><span class="w"> </span><span class="kr">type</span><span class="o">=</span><span class="s2">"text"</span>
|
||
</span><span id="__span-8-66"><a id="__codelineno-8-66" name="__codelineno-8-66" href="#__codelineno-8-66"></a><span class="w"> </span><span class="nx">icon</span><span class="o">=</span><span class="p">{</span><span class="nx">hasUpvoted</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="o"><</span><span class="nx">LikeFilled</span><span class="w"> </span><span class="o">/></span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="o"><</span><span class="nx">LikeOutlined</span><span class="w"> </span><span class="o">/></span><span class="p">}</span>
|
||
</span><span id="__span-8-67"><a id="__codelineno-8-67" name="__codelineno-8-67" href="#__codelineno-8-67"></a><span class="w"> </span><span class="nx">onClick</span><span class="o">=</span><span class="p">{()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">onUpvote</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">id</span><span class="p">)}</span>
|
||
</span><span id="__span-8-68"><a id="__codelineno-8-68" name="__codelineno-8-68" href="#__codelineno-8-68"></a><span class="w"> </span><span class="nx">disabled</span><span class="o">=</span><span class="p">{</span><span class="nx">hasUpvoted</span><span class="p">}</span>
|
||
</span><span id="__span-8-69"><a id="__codelineno-8-69" name="__codelineno-8-69" href="#__codelineno-8-69"></a><span class="w"> </span><span class="o">></span>
|
||
</span><span id="__span-8-70"><a id="__codelineno-8-70" name="__codelineno-8-70" href="#__codelineno-8-70"></a><span class="w"> </span><span class="p">{</span><span class="nx">response</span><span class="p">.</span><span class="nx">upvotes</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="nx">response</span><span class="p">.</span><span class="nx">upvotes</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">1</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="s1">'upvote'</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="s1">'upvotes'</span><span class="p">}</span>
|
||
</span><span id="__span-8-71"><a id="__codelineno-8-71" name="__codelineno-8-71" href="#__codelineno-8-71"></a><span class="w"> </span><span class="o"><</span><span class="err">/Button></span>
|
||
</span><span id="__span-8-72"><a id="__codelineno-8-72" name="__codelineno-8-72" href="#__codelineno-8-72"></a><span class="w"> </span><span class="o"><</span><span class="err">/Space></span>
|
||
</span><span id="__span-8-73"><a id="__codelineno-8-73" name="__codelineno-8-73" href="#__codelineno-8-73"></a><span class="w"> </span><span class="o"><</span><span class="err">/Card></span>
|
||
</span><span id="__span-8-74"><a id="__codelineno-8-74" name="__codelineno-8-74" href="#__codelineno-8-74"></a><span class="w"> </span><span class="p">);</span>
|
||
</span><span id="__span-8-75"><a id="__codelineno-8-75" name="__codelineno-8-75" href="#__codelineno-8-75"></a><span class="p">};</span>
|
||
</span><span id="__span-8-76"><a id="__codelineno-8-76" name="__codelineno-8-76" href="#__codelineno-8-76"></a>
|
||
</span><span id="__span-8-77"><a id="__codelineno-8-77" name="__codelineno-8-77" href="#__codelineno-8-77"></a><span class="k">export</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="nx">ResponseCard</span><span class="p">;</span>
|
||
</span></code></pre></div>
|
||
<h2 id="troubleshooting">Troubleshooting<a class="headerlink" href="#troubleshooting" title="Permanent link">¶</a></h2>
|
||
<h3 id="verification-email-not-received">Verification Email Not Received<a class="headerlink" href="#verification-email-not-received" title="Permanent link">¶</a></h3>
|
||
<p><strong>Symptoms:</strong>
|
||
- User doesn't receive verification email
|
||
- Email not in spam folder</p>
|
||
<p><strong>Solutions:</strong></p>
|
||
<ol>
|
||
<li>Check email service logs → <code>docker compose logs api | grep "verification"</code></li>
|
||
<li>Verify SMTP configuration → test with <code>/api/auth/test-email</code></li>
|
||
<li>Check EMAIL_TEST_MODE → if true, email sent to MailHog (localhost:8025)</li>
|
||
<li>Resend verification email → manual resend via admin UI</li>
|
||
</ol>
|
||
<p><strong>Manual Resend:</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">// Admin UI: ResponsesPage</span>
|
||
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a><span class="kd">const</span><span class="w"> </span><span class="nx">handleResendVerification</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">responseId</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-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a><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">`/responses/</span><span class="si">${</span><span class="nx">responseId</span><span class="si">}</span><span class="sb">/resend-verification`</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="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">success</span><span class="p">(</span><span class="s1">'Verification email resent'</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="p">};</span>
|
||
</span></code></pre></div>
|
||
<h3 id="duplicate-upvotes">Duplicate Upvotes<a class="headerlink" href="#duplicate-upvotes" title="Permanent link">¶</a></h3>
|
||
<p><strong>Symptoms:</strong>
|
||
- User can upvote same response multiple times
|
||
- Upvote count inflated</p>
|
||
<p><strong>Solutions:</strong></p>
|
||
<ol>
|
||
<li>Check database constraints → should have unique constraint on <code>responseId, ipAddress</code></li>
|
||
<li>Verify transaction → upvote creation and count increment must be atomic</li>
|
||
<li>Check IP address extraction → ensure <code>req.ip</code> is correct (consider X-Forwarded-For)</li>
|
||
</ol>
|
||
<p><strong>Database Fix:</strong></p>
|
||
<div class="language-sql 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="c1">-- Add unique constraint if missing</span>
|
||
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a><span class="k">ALTER</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">response_upvotes</span>
|
||
</span><span id="__span-10-3"><a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a><span class="k">ADD</span><span class="w"> </span><span class="k">CONSTRAINT</span><span class="w"> </span><span class="n">unique_response_ip</span>
|
||
</span><span id="__span-10-4"><a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a><span class="k">UNIQUE</span><span class="w"> </span><span class="p">(</span><span class="n">response_id</span><span class="p">,</span><span class="w"> </span><span class="n">ip_address</span><span class="p">);</span>
|
||
</span><span id="__span-10-5"><a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a>
|
||
</span><span id="__span-10-6"><a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a><span class="k">ALTER</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">response_upvotes</span>
|
||
</span><span id="__span-10-7"><a id="__codelineno-10-7" name="__codelineno-10-7" href="#__codelineno-10-7"></a><span class="k">ADD</span><span class="w"> </span><span class="k">CONSTRAINT</span><span class="w"> </span><span class="n">unique_response_user</span>
|
||
</span><span id="__span-10-8"><a id="__codelineno-10-8" name="__codelineno-10-8" href="#__codelineno-10-8"></a><span class="k">UNIQUE</span><span class="w"> </span><span class="p">(</span><span class="n">response_id</span><span class="p">,</span><span class="w"> </span><span class="n">user_id</span><span class="p">)</span>
|
||
</span><span id="__span-10-9"><a id="__codelineno-10-9" name="__codelineno-10-9" href="#__codelineno-10-9"></a><span class="k">WHERE</span><span class="w"> </span><span class="n">user_id</span><span class="w"> </span><span class="k">IS</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">;</span>
|
||
</span></code></pre></div>
|
||
<h3 id="screenshot-upload-fails">Screenshot Upload Fails<a class="headerlink" href="#screenshot-upload-fails" title="Permanent link">¶</a></h3>
|
||
<p><strong>Symptoms:</strong>
|
||
- Upload spinner never completes
|
||
- Error: "File too large"</p>
|
||
<p><strong>Solutions:</strong></p>
|
||
<ol>
|
||
<li>Check file size → max 5MB</li>
|
||
<li>Verify file format → must be image (jpg/jpeg/png/gif/webp)</li>
|
||
<li>Check upload directory permissions → <code>/uploads/responses</code> must be writable</li>
|
||
<li>Increase Nginx upload limit → <code>client_max_body_size 10M;</code></li>
|
||
</ol>
|
||
<p><strong>Code Fix (responses.service.ts):</strong></p>
|
||
<div class="language-typescript 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="k">import</span><span class="w"> </span><span class="nx">sharp</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'sharp'</span><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><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a><span class="k">async</span><span class="w"> </span><span class="nx">uploadScreenshot</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="nx">file</span><span class="o">:</span><span class="w"> </span><span class="kt">Express.Multer.File</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="nx">responseId</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span>
|
||
</span><span id="__span-11-6"><a id="__codelineno-11-6" name="__codelineno-11-6" href="#__codelineno-11-6"></a><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="kt">string</span><span class="o">></span><span class="w"> </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="kd">const</span><span class="w"> </span><span class="nx">uploadDir</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`/uploads/responses/</span><span class="si">${</span><span class="nx">responseId</span><span class="si">}</span><span class="sb">`</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="k">await</span><span class="w"> </span><span class="nx">fs</span><span class="p">.</span><span class="nx">mkdir</span><span class="p">(</span><span class="nx">uploadDir</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">recursive</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-11-9"><a id="__codelineno-11-9" name="__codelineno-11-9" href="#__codelineno-11-9"></a>
|
||
</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="kd">const</span><span class="w"> </span><span class="nx">filename</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`</span><span class="si">${</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span><span class="si">}</span><span class="sb">-</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">originalname</span><span class="si">}</span><span class="sb">`</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><span id="__span-11-12"><a id="__codelineno-11-12" name="__codelineno-11-12" href="#__codelineno-11-12"></a><span class="w"> </span><span class="c1">// Optimize image (max 1200px width, 85% quality)</span>
|
||
</span><span id="__span-11-13"><a id="__codelineno-11-13" name="__codelineno-11-13" href="#__codelineno-11-13"></a><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">sharp</span><span class="p">(</span><span class="nx">file</span><span class="p">.</span><span class="nx">buffer</span><span class="p">)</span>
|
||
</span><span id="__span-11-14"><a id="__codelineno-11-14" name="__codelineno-11-14" href="#__codelineno-11-14"></a><span class="w"> </span><span class="p">.</span><span class="nx">resize</span><span class="p">(</span><span class="mf">1200</span><span class="p">,</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">withoutEnlargement</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-11-15"><a id="__codelineno-11-15" name="__codelineno-11-15" href="#__codelineno-11-15"></a><span class="w"> </span><span class="p">.</span><span class="nx">jpeg</span><span class="p">({</span><span class="w"> </span><span class="nx">quality</span><span class="o">:</span><span class="w"> </span><span class="kt">85</span><span class="w"> </span><span class="p">})</span>
|
||
</span><span id="__span-11-16"><a id="__codelineno-11-16" name="__codelineno-11-16" href="#__codelineno-11-16"></a><span class="w"> </span><span class="p">.</span><span class="nx">toFile</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">uploadDir</span><span class="si">}</span><span class="sb">/</span><span class="si">${</span><span class="nx">filename</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
|
||
</span><span id="__span-11-17"><a id="__codelineno-11-17" name="__codelineno-11-17" href="#__codelineno-11-17"></a>
|
||
</span><span id="__span-11-18"><a id="__codelineno-11-18" name="__codelineno-11-18" href="#__codelineno-11-18"></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">uploadDir</span><span class="si">}</span><span class="sb">/</span><span class="si">${</span><span class="nx">filename</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
|
||
</span><span id="__span-11-19"><a id="__codelineno-11-19" name="__codelineno-11-19" href="#__codelineno-11-19"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<h2 id="performance-considerations">Performance Considerations<a class="headerlink" href="#performance-considerations" title="Permanent link">¶</a></h2>
|
||
<h3 id="query-optimization">Query Optimization<a class="headerlink" href="#query-optimization" title="Permanent link">¶</a></h3>
|
||
<p><strong>Index Strategy:</strong></p>
|
||
<div class="language-sql highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="c1">-- Composite index for public wall queries</span>
|
||
</span><span id="__span-12-2"><a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a><span class="k">CREATE</span><span class="w"> </span><span class="k">INDEX</span><span class="w"> </span><span class="n">idx_response_campaign_status</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="k">ON</span><span class="w"> </span><span class="n">responses</span><span class="w"> </span><span class="p">(</span><span class="n">campaign_id</span><span class="p">,</span><span class="w"> </span><span class="n">status</span><span class="p">)</span>
|
||
</span><span id="__span-12-4"><a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a><span class="w"> </span><span class="k">WHERE</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">'APPROVED'</span><span class="p">;</span>
|
||
</span><span id="__span-12-5"><a id="__codelineno-12-5" name="__codelineno-12-5" href="#__codelineno-12-5"></a>
|
||
</span><span id="__span-12-6"><a id="__codelineno-12-6" name="__codelineno-12-6" href="#__codelineno-12-6"></a><span class="c1">-- Index for sorting by upvotes</span>
|
||
</span><span id="__span-12-7"><a id="__codelineno-12-7" name="__codelineno-12-7" href="#__codelineno-12-7"></a><span class="k">CREATE</span><span class="w"> </span><span class="k">INDEX</span><span class="w"> </span><span class="n">idx_response_upvotes</span>
|
||
</span><span id="__span-12-8"><a id="__codelineno-12-8" name="__codelineno-12-8" href="#__codelineno-12-8"></a><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">responses</span><span class="w"> </span><span class="p">(</span><span class="n">upvotes</span><span class="w"> </span><span class="k">DESC</span><span class="p">);</span>
|
||
</span><span id="__span-12-9"><a id="__codelineno-12-9" name="__codelineno-12-9" href="#__codelineno-12-9"></a>
|
||
</span><span id="__span-12-10"><a id="__codelineno-12-10" name="__codelineno-12-10" href="#__codelineno-12-10"></a><span class="c1">-- Index for email verification lookups</span>
|
||
</span><span id="__span-12-11"><a id="__codelineno-12-11" name="__codelineno-12-11" href="#__codelineno-12-11"></a><span class="k">CREATE</span><span class="w"> </span><span class="k">INDEX</span><span class="w"> </span><span class="n">idx_response_verification_token</span>
|
||
</span><span id="__span-12-12"><a id="__codelineno-12-12" name="__codelineno-12-12" href="#__codelineno-12-12"></a><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">responses</span><span class="w"> </span><span class="p">(</span><span class="n">verification_token</span><span class="p">)</span>
|
||
</span><span id="__span-12-13"><a id="__codelineno-12-13" name="__codelineno-12-13" href="#__codelineno-12-13"></a><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">verification_token</span><span class="w"> </span><span class="k">IS</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">;</span>
|
||
</span></code></pre></div>
|
||
<p><strong>Optimized Public Query:</strong></p>
|
||
<div class="language-typescript 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="kd">const</span><span class="w"> </span><span class="nx">responses</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">response</span><span class="p">.</span><span class="nx">findMany</span><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="nx">where</span><span class="o">:</span><span class="w"> </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="nx">campaignId</span><span class="p">,</span>
|
||
</span><span id="__span-13-4"><a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="s1">'APPROVED'</span><span class="p">,</span>
|
||
</span><span id="__span-13-5"><a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a><span class="w"> </span><span class="nx">isEmailVerified</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span>
|
||
</span><span id="__span-13-6"><a id="__codelineno-13-6" name="__codelineno-13-6" href="#__codelineno-13-6"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-13-7"><a id="__codelineno-13-7" name="__codelineno-13-7" href="#__codelineno-13-7"></a><span class="w"> </span><span class="nx">orderBy</span><span class="o">:</span><span class="w"> </span><span class="p">[</span>
|
||
</span><span id="__span-13-8"><a id="__codelineno-13-8" name="__codelineno-13-8" href="#__codelineno-13-8"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">upvotes</span><span class="o">:</span><span class="w"> </span><span class="s1">'desc'</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-13-9"><a id="__codelineno-13-9" name="__codelineno-13-9" href="#__codelineno-13-9"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">createdAt</span><span class="o">:</span><span class="w"> </span><span class="s1">'desc'</span><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-13-10"><a id="__codelineno-13-10" name="__codelineno-13-10" href="#__codelineno-13-10"></a><span class="w"> </span><span class="p">],</span>
|
||
</span><span id="__span-13-11"><a id="__codelineno-13-11" name="__codelineno-13-11" href="#__codelineno-13-11"></a><span class="w"> </span><span class="nx">take</span><span class="o">:</span><span class="w"> </span><span class="kt">50</span><span class="p">,</span>
|
||
</span><span id="__span-13-12"><a id="__codelineno-13-12" name="__codelineno-13-12" href="#__codelineno-13-12"></a><span class="w"> </span><span class="nx">skip</span><span class="o">:</span><span class="w"> </span><span class="kt">page</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">50</span>
|
||
</span><span id="__span-13-13"><a id="__codelineno-13-13" name="__codelineno-13-13" href="#__codelineno-13-13"></a><span class="p">});</span>
|
||
</span></code></pre></div>
|
||
<h3 id="caching-strategy">Caching Strategy<a class="headerlink" href="#caching-strategy" title="Permanent link">¶</a></h3>
|
||
<p><strong>Redis Caching for Response Wall:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">redisClient</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">'../../../config/redis'</span><span class="p">;</span>
|
||
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a>
|
||
</span><span id="__span-14-3"><a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a><span class="k">async</span><span class="w"> </span><span class="nx">getApprovedResponses</span><span class="p">(</span><span class="nx">campaignId</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">Response</span><span class="p">[]</span><span class="o">></span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-14-4"><a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">cacheKey</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`responses:</span><span class="si">${</span><span class="nx">campaignId</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
|
||
</span><span id="__span-14-5"><a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a>
|
||
</span><span id="__span-14-6"><a id="__codelineno-14-6" name="__codelineno-14-6" href="#__codelineno-14-6"></a><span class="w"> </span><span class="c1">// Check cache</span>
|
||
</span><span id="__span-14-7"><a id="__codelineno-14-7" name="__codelineno-14-7" href="#__codelineno-14-7"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">cached</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">redisClient</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">cacheKey</span><span class="p">);</span>
|
||
</span><span id="__span-14-8"><a id="__codelineno-14-8" name="__codelineno-14-8" href="#__codelineno-14-8"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">cached</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-14-9"><a id="__codelineno-14-9" name="__codelineno-14-9" href="#__codelineno-14-9"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">cached</span><span class="p">);</span>
|
||
</span><span id="__span-14-10"><a id="__codelineno-14-10" name="__codelineno-14-10" href="#__codelineno-14-10"></a><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-14-11"><a id="__codelineno-14-11" name="__codelineno-14-11" href="#__codelineno-14-11"></a>
|
||
</span><span id="__span-14-12"><a id="__codelineno-14-12" name="__codelineno-14-12" href="#__codelineno-14-12"></a><span class="w"> </span><span class="c1">// Query database</span>
|
||
</span><span id="__span-14-13"><a id="__codelineno-14-13" name="__codelineno-14-13" href="#__codelineno-14-13"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">responses</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">response</span><span class="p">.</span><span class="nx">findMany</span><span class="p">({</span>
|
||
</span><span id="__span-14-14"><a id="__codelineno-14-14" name="__codelineno-14-14" href="#__codelineno-14-14"></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-14-15"><a id="__codelineno-14-15" name="__codelineno-14-15" href="#__codelineno-14-15"></a><span class="w"> </span><span class="nx">campaignId</span><span class="p">,</span>
|
||
</span><span id="__span-14-16"><a id="__codelineno-14-16" name="__codelineno-14-16" href="#__codelineno-14-16"></a><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="s1">'APPROVED'</span><span class="p">,</span>
|
||
</span><span id="__span-14-17"><a id="__codelineno-14-17" name="__codelineno-14-17" href="#__codelineno-14-17"></a><span class="w"> </span><span class="nx">isEmailVerified</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span>
|
||
</span><span id="__span-14-18"><a id="__codelineno-14-18" name="__codelineno-14-18" href="#__codelineno-14-18"></a><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-14-19"><a id="__codelineno-14-19" name="__codelineno-14-19" href="#__codelineno-14-19"></a><span class="w"> </span><span class="nx">orderBy</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">upvotes</span><span class="o">:</span><span class="w"> </span><span class="s1">'desc'</span><span class="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-14-20"><a id="__codelineno-14-20" name="__codelineno-14-20" href="#__codelineno-14-20"></a><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-14-21"><a id="__codelineno-14-21" name="__codelineno-14-21" href="#__codelineno-14-21"></a>
|
||
</span><span id="__span-14-22"><a id="__codelineno-14-22" name="__codelineno-14-22" href="#__codelineno-14-22"></a><span class="w"> </span><span class="c1">// Cache for 5 minutes</span>
|
||
</span><span id="__span-14-23"><a id="__codelineno-14-23" name="__codelineno-14-23" href="#__codelineno-14-23"></a><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">redisClient</span><span class="p">.</span><span class="nx">setex</span><span class="p">(</span><span class="nx">cacheKey</span><span class="p">,</span><span class="w"> </span><span class="mf">300</span><span class="p">,</span><span class="w"> </span><span class="nb">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">responses</span><span class="p">));</span>
|
||
</span><span id="__span-14-24"><a id="__codelineno-14-24" name="__codelineno-14-24" href="#__codelineno-14-24"></a>
|
||
</span><span id="__span-14-25"><a id="__codelineno-14-25" name="__codelineno-14-25" href="#__codelineno-14-25"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">responses</span><span class="p">;</span>
|
||
</span><span id="__span-14-26"><a id="__codelineno-14-26" name="__codelineno-14-26" href="#__codelineno-14-26"></a><span class="p">}</span>
|
||
</span><span id="__span-14-27"><a id="__codelineno-14-27" name="__codelineno-14-27" href="#__codelineno-14-27"></a>
|
||
</span><span id="__span-14-28"><a id="__codelineno-14-28" name="__codelineno-14-28" href="#__codelineno-14-28"></a><span class="c1">// Invalidate cache on moderation</span>
|
||
</span><span id="__span-14-29"><a id="__codelineno-14-29" name="__codelineno-14-29" href="#__codelineno-14-29"></a><span class="k">async</span><span class="w"> </span><span class="nx">moderateResponse</span><span class="p">(</span><span class="nx">responseId</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">status</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><span id="__span-14-30"><a id="__codelineno-14-30" name="__codelineno-14-30" href="#__codelineno-14-30"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">response</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">response</span><span class="p">.</span><span class="nx">update</span><span class="p">({</span>
|
||
</span><span id="__span-14-31"><a id="__codelineno-14-31" name="__codelineno-14-31" href="#__codelineno-14-31"></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">responseId</span><span class="w"> </span><span class="p">},</span>
|
||
</span><span id="__span-14-32"><a id="__codelineno-14-32" name="__codelineno-14-32" href="#__codelineno-14-32"></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="w"> </span><span class="p">}</span>
|
||
</span><span id="__span-14-33"><a id="__codelineno-14-33" name="__codelineno-14-33" href="#__codelineno-14-33"></a><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-14-34"><a id="__codelineno-14-34" name="__codelineno-14-34" href="#__codelineno-14-34"></a>
|
||
</span><span id="__span-14-35"><a id="__codelineno-14-35" name="__codelineno-14-35" href="#__codelineno-14-35"></a><span class="w"> </span><span class="c1">// Invalidate cache</span>
|
||
</span><span id="__span-14-36"><a id="__codelineno-14-36" name="__codelineno-14-36" href="#__codelineno-14-36"></a><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">redisClient</span><span class="p">.</span><span class="nx">del</span><span class="p">(</span><span class="sb">`responses:</span><span class="si">${</span><span class="nx">response</span><span class="p">.</span><span class="nx">campaignId</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
|
||
</span><span id="__span-14-37"><a id="__codelineno-14-37" name="__codelineno-14-37" href="#__codelineno-14-37"></a>
|
||
</span><span id="__span-14-38"><a id="__codelineno-14-38" name="__codelineno-14-38" href="#__codelineno-14-38"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">response</span><span class="p">;</span>
|
||
</span><span id="__span-14-39"><a id="__codelineno-14-39" name="__codelineno-14-39" href="#__codelineno-14-39"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<h3 id="screenshot-optimization">Screenshot Optimization<a class="headerlink" href="#screenshot-optimization" title="Permanent link">¶</a></h3>
|
||
<p><strong>Image Processing Pipeline:</strong></p>
|
||
<div class="language-typescript 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="k">import</span><span class="w"> </span><span class="nx">sharp</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">'sharp'</span><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><span id="__span-15-3"><a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a><span class="k">async</span><span class="w"> </span><span class="nx">optimizeScreenshot</span><span class="p">(</span><span class="nx">file</span><span class="o">:</span><span class="w"> </span><span class="kt">Express.Multer.File</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">Buffer</span><span class="o">></span><span class="w"> </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="k">return</span><span class="w"> </span><span class="nx">sharp</span><span class="p">(</span><span class="nx">file</span><span class="p">.</span><span class="nx">buffer</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="p">.</span><span class="nx">resize</span><span class="p">(</span><span class="mf">1200</span><span class="p">,</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> </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="nx">withoutEnlargement</span><span class="o">:</span><span class="w"> </span><span class="kt">true</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="nx">fit</span><span class="o">:</span><span class="w"> </span><span class="s1">'inside'</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="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="p">.</span><span class="nx">jpeg</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="nx">quality</span><span class="o">:</span><span class="w"> </span><span class="kt">85</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="nx">progressive</span><span class="o">:</span><span class="w"> </span><span class="kt">true</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="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="p">.</span><span class="nx">toBuffer</span><span class="p">();</span>
|
||
</span><span id="__span-15-14"><a id="__codelineno-15-14" name="__codelineno-15-14" href="#__codelineno-15-14"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<p><strong>CDN Integration:</strong></p>
|
||
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-16-1"><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a><span class="c1">// Upload optimized screenshots to CDN</span>
|
||
</span><span id="__span-16-2"><a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">S3Client</span><span class="p">,</span><span class="w"> </span><span class="nx">PutObjectCommand</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">'@aws-sdk/client-s3'</span><span class="p">;</span>
|
||
</span><span id="__span-16-3"><a id="__codelineno-16-3" name="__codelineno-16-3" href="#__codelineno-16-3"></a>
|
||
</span><span id="__span-16-4"><a id="__codelineno-16-4" name="__codelineno-16-4" href="#__codelineno-16-4"></a><span class="kd">const</span><span class="w"> </span><span class="nx">s3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">S3Client</span><span class="p">({</span><span class="w"> </span><span class="nx">region</span><span class="o">:</span><span class="w"> </span><span class="s1">'us-east-1'</span><span class="w"> </span><span class="p">});</span>
|
||
</span><span id="__span-16-5"><a id="__codelineno-16-5" name="__codelineno-16-5" href="#__codelineno-16-5"></a>
|
||
</span><span id="__span-16-6"><a id="__codelineno-16-6" name="__codelineno-16-6" href="#__codelineno-16-6"></a><span class="k">async</span><span class="w"> </span><span class="nx">uploadToCDN</span><span class="p">(</span><span class="nx">buffer</span><span class="o">:</span><span class="w"> </span><span class="kt">Buffer</span><span class="p">,</span><span class="w"> </span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="kt">string</span><span class="o">></span><span class="w"> </span><span class="p">{</span>
|
||
</span><span id="__span-16-7"><a id="__codelineno-16-7" name="__codelineno-16-7" href="#__codelineno-16-7"></a><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">s3</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="ow">new</span><span class="w"> </span><span class="nx">PutObjectCommand</span><span class="p">({</span>
|
||
</span><span id="__span-16-8"><a id="__codelineno-16-8" name="__codelineno-16-8" href="#__codelineno-16-8"></a><span class="w"> </span><span class="nx">Bucket</span><span class="o">:</span><span class="w"> </span><span class="kt">process.env.S3_BUCKET</span><span class="p">,</span>
|
||
</span><span id="__span-16-9"><a id="__codelineno-16-9" name="__codelineno-16-9" href="#__codelineno-16-9"></a><span class="w"> </span><span class="nx">Key</span><span class="o">:</span><span class="w"> </span><span class="sb">`responses/</span><span class="si">${</span><span class="nx">key</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span>
|
||
</span><span id="__span-16-10"><a id="__codelineno-16-10" name="__codelineno-16-10" href="#__codelineno-16-10"></a><span class="w"> </span><span class="nx">Body</span><span class="o">:</span><span class="w"> </span><span class="kt">buffer</span><span class="p">,</span>
|
||
</span><span id="__span-16-11"><a id="__codelineno-16-11" name="__codelineno-16-11" href="#__codelineno-16-11"></a><span class="w"> </span><span class="nx">ContentType</span><span class="o">:</span><span class="w"> </span><span class="s1">'image/jpeg'</span><span class="p">,</span>
|
||
</span><span id="__span-16-12"><a id="__codelineno-16-12" name="__codelineno-16-12" href="#__codelineno-16-12"></a><span class="w"> </span><span class="nx">CacheControl</span><span class="o">:</span><span class="w"> </span><span class="s1">'max-age=31536000'</span><span class="w"> </span><span class="c1">// 1 year</span>
|
||
</span><span id="__span-16-13"><a id="__codelineno-16-13" name="__codelineno-16-13" href="#__codelineno-16-13"></a><span class="w"> </span><span class="p">}));</span>
|
||
</span><span id="__span-16-14"><a id="__codelineno-16-14" name="__codelineno-16-14" href="#__codelineno-16-14"></a>
|
||
</span><span id="__span-16-15"><a id="__codelineno-16-15" name="__codelineno-16-15" href="#__codelineno-16-15"></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">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">CDN_URL</span><span class="si">}</span><span class="sb">/responses/</span><span class="si">${</span><span class="nx">key</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
|
||
</span><span id="__span-16-16"><a id="__codelineno-16-16" name="__codelineno-16-16" href="#__codelineno-16-16"></a><span class="p">}</span>
|
||
</span></code></pre></div>
|
||
<h2 id="related-documentation">Related Documentation<a class="headerlink" href="#related-documentation" title="Permanent link">¶</a></h2>
|
||
<h3 id="backend-modules">Backend Modules<a class="headerlink" href="#backend-modules" title="Permanent link">¶</a></h3>
|
||
<ul>
|
||
<li><a href="../../../backend/modules/responses/">Responses Module</a> — Full API reference</li>
|
||
<li><a href="../../../backend/modules/campaigns/">Campaigns Module</a> — Campaign integration</li>
|
||
<li><a href="../../backend/modules/email.md">Email Service</a> — Email verification</li>
|
||
</ul>
|
||
<h3 id="frontend-pages">Frontend Pages<a class="headerlink" href="#frontend-pages" title="Permanent link">¶</a></h3>
|
||
<ul>
|
||
<li><a href="../../../frontend/pages/admin/responses-page/">ResponsesPage</a> — Admin moderation</li>
|
||
<li><a href="../../../frontend/pages/public/response-wall-page/">ResponseWallPage</a> — Public response wall</li>
|
||
</ul>
|
||
<h3 id="database-models_1">Database Models<a class="headerlink" href="#database-models_1" title="Permanent link">¶</a></h3>
|
||
<ul>
|
||
<li><a href="../../database/models/response.md">Response</a> — Response schema</li>
|
||
<li><a href="../../database/models/response-upvote.md">ResponseUpvote</a> — Upvote tracking schema</li>
|
||
<li><a href="../../database/models/campaign.md">Campaign</a> — Campaign schema</li>
|
||
</ul>
|
||
<h3 id="guides">Guides<a class="headerlink" href="#guides" title="Permanent link">¶</a></h3>
|
||
<ul>
|
||
<li><a href="../campaigns/">Campaign Management</a> — Campaign configuration</li>
|
||
<li><a href="../../email-templates/template-system/">Email Templates</a> — Verification email templates</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="../postal-codes/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Postal Codes">
|
||
<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">
|
||
Postal Codes
|
||
</div>
|
||
</div>
|
||
</a>
|
||
|
||
|
||
|
||
<a href="../email-queue/" class="md-footer__link md-footer__link--next" aria-label="Next: Email Queue">
|
||
<div class="md-footer__title">
|
||
<span class="md-footer__direction">
|
||
Next
|
||
</span>
|
||
<div class="md-ellipsis">
|
||
Email Queue
|
||
</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> |