ekyc

Electronic KYC process with uploading ID document using OAuth 2.1 (experimental)
Log | Files | Refs | README | LICENSE

id-document.tsx (4861B)


      1 import { IDDocumentListResponse } from "#core/application/id_document/list.ts";
      2 import { AppState } from "#http/routes/_middleware.ts";
      3 import { PageProps } from "$fresh/server.ts";
      4 import { Handlers } from "$fresh/src/server/types.ts";
      5 import * as V from "$valita";
      6 
      7 type Props = {
      8   items: IDDocumentListResponse["items"];
      9   next: URL;
     10 };
     11 
     12 export const handler: Handlers<Props, AppState<"/verify/id-document">> = {
     13   async GET(_req, ctx) {
     14     const { app, forms, formContext } = ctx.state;
     15     const { idDocumentList, isIdDocumentAdmin } = app;
     16     if (formContext === null) {
     17       return forms.redirect({
     18         form: "/verify/id-document",
     19         context: { cursor: 0 },
     20       });
     21     }
     22     const { cursor } = formContext;
     23     if (forms.session === null) {
     24       return forms.redirect({
     25         form: "/connect",
     26         context: {
     27           back: { form: "/verify/id-document", context: formContext },
     28         },
     29       });
     30     }
     31     if (!(await isIdDocumentAdmin.execute({ uuid: forms.session.uuid }))) {
     32       return forms.redirect("/");
     33     }
     34     const { items, next: nextCursor } = await idDocumentList.execute({
     35       cursor,
     36     });
     37     const next = forms.link({
     38       form: "/verify/id-document",
     39       context: { cursor: nextCursor },
     40     });
     41     return ctx.render({ items, next });
     42   },
     43 
     44   async POST(req, ctx) {
     45     const { app, forms, formContext } = ctx.state;
     46     const { idDocumentApprove, idDocumentDecline, isIdDocumentAdmin } = app;
     47     if (formContext === null) {
     48       return forms.redirect("/");
     49     }
     50 
     51     if (forms.session === null) {
     52       return forms.redirect({
     53         form: "/connect",
     54         context: {
     55           back: { form: "/verify/id-document", context: formContext },
     56         },
     57       });
     58     }
     59     if (!(await isIdDocumentAdmin.execute({ uuid: forms.session.uuid }))) {
     60       return forms.redirect("/");
     61     }
     62 
     63     const { uuid, approved } = await forms.inputs(
     64       req,
     65       V.object({
     66         uuid: V.string(),
     67         approved: V.string().map(() => true).default(false),
     68       }),
     69     );
     70 
     71     if (approved) {
     72       await idDocumentApprove.execute({
     73         admin: forms.session.uuid,
     74         uuid,
     75       });
     76     } else {
     77       await idDocumentDecline.execute({
     78         admin: forms.session.uuid,
     79         uuid,
     80       });
     81     }
     82 
     83     return forms.redirect(req.url);
     84   },
     85 };
     86 
     87 export default function IDDocumentPage(
     88   { data }: PageProps<Props, AppState<"/verify/id-document">>,
     89 ) {
     90   return (
     91     <>
     92     <div class="grid">
     93       {data.items.map((i) => (
     94         <article style="max-width: 32em;">
     95           <header style="text-align: center;">
     96             <b>ID Information</b>
     97           </header>
     98           <div>
     99             <table>
    100               <tr>
    101                 <th>
    102                   <b>First name</b>
    103                 </th>
    104                 <td>{i.firstName ? i.firstName : "—"}</td>
    105               </tr>
    106               <tr>
    107                 <th>
    108                   <b>Last name</b>
    109                 </th>
    110                 <td>{i.lastName ? i.lastName : "—"}</td>
    111               </tr>
    112               <tr>
    113                 <th>
    114                   <b>Sex</b>
    115                 </th>
    116                 <td>{i.sex ? i.sex : "—"}</td>
    117               </tr>
    118               <tr>
    119                 <th>
    120                   <b>Birth date</b>
    121                 </th>
    122                 <td>
    123                   {i.birthDate
    124                     ? i.birthDate.toLocaleDateString("en", {
    125                       year: "numeric",
    126                       month: "long",
    127                       day: "numeric",
    128                     })
    129                     : "—"}
    130                 </td>
    131               </tr>
    132               <tr>
    133                 <th>
    134                   <b>Nationality</b>
    135                 </th>
    136                 <td>{i.nationality ? i.nationality : "—"}</td>
    137               </tr>
    138               <tr>
    139                 <th>
    140                   <b>Country</b>
    141                 </th>
    142                 <td>{i.country ? i.country : "—"}</td>
    143               </tr>
    144             </table>
    145             <section className="grid">
    146               {i.docFront && <img src={i.docFront} />}
    147               {i.docBack && <img src={i.docBack} />}
    148               {i.faceLeft && <img src={i.faceLeft} />}
    149               {i.faceFront && <img src={i.faceFront} />}
    150               {i.faceRight && <img src={i.faceRight} />}
    151             </section>
    152             <form method="POST">
    153               <input type="hidden" name="uuid" value={i.uuid} />
    154               <div role="group">
    155                 <button type="submit" class="secondary">Decline</button>
    156                 <button type="submit" name="approved" value="on">
    157                   Approve
    158                 </button>
    159               </div>
    160             </form>
    161           </div>
    162         </article>
    163       ))}
    164       
    165     </div>
    166     <nav role="group">
    167       <a role="button" href={data.next.href}>Next</a>
    168     </nav>
    169     </>
    170   );
    171 }