conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

projects.tsx (4095B)


      1 // web/src/screen/projects.tsx - the project list, and creating one
      2 
      3 import m from 'mithril';
      4 import { page, post, Page } from '../util/api';
      5 import { Empty, Failed, Loading, Pager, When, session } from '../component/layout';
      6 
      7 const LIMIT = 25;
      8 
      9 type Project = {
     10   id: string; name: string; repo_url: string; default_branch: string;
     11   visibility: string; enabled: number; job_counter: number; created_at: number;
     12 };
     13 
     14 export const Projects: m.Component = {
     15   oninit(vnode: any) {
     16     vnode.state.offset = 0;
     17     vnode.state.rows = null as Page<Project> | null;
     18     vnode.state.error = null;
     19     vnode.state.creating = false;
     20     vnode.state.created = null;
     21     vnode.state.form = { id: '', name: '', repo_url: '', default_branch: 'main' };
     22 
     23     vnode.state.load = async () => {
     24       try {
     25         vnode.state.rows = await page<Project>('/projects', {
     26           query: { offset: vnode.state.offset, limit: LIMIT },
     27         });
     28         vnode.state.error = null;
     29       } catch (e) {
     30         vnode.state.error = e;
     31       }
     32       m.redraw();
     33     };
     34     vnode.state.load();
     35   },
     36 
     37   view(vnode: any) {
     38     const { rows, error, created } = vnode.state;
     39     if (error && !rows) return <Failed error={error} />;
     40     if (!rows) return <Loading />;
     41 
     42     const create = async (e: Event) => {
     43       e.preventDefault();
     44       try {
     45         vnode.state.created = await post<any>('/projects', vnode.state.form);
     46         vnode.state.creating = false;
     47         vnode.state.form = { id: '', name: '', repo_url: '', default_branch: 'main' };
     48         await vnode.state.load();
     49       } catch (err) {
     50         vnode.state.error = err;
     51       }
     52     };
     53 
     54     const field = (key: string, label: string, required = false) => (
     55       <label>
     56         <span>{label}</span>
     57         <input
     58           value={vnode.state.form[key]}
     59           required={required}
     60           oninput={(e: any) => { vnode.state.form[key] = e.target.value; }}
     61         />
     62       </label>
     63     );
     64 
     65     return [
     66       <h1>projects</h1>,
     67       error ? <Failed error={error} /> : null,
     68 
     69       // Shown once, because it is never readable again.
     70       created?.trigger_secret
     71         ? (
     72           <div class="reveal">
     73             <strong>Trigger secret for {created.id}</strong>
     74             <p class="muted">Copy it now. It cannot be shown again.</p>
     75             <code class="mono">{created.trigger_secret}</code>
     76             <button onclick={() => { vnode.state.created = null; }}>done</button>
     77           </div>
     78         )
     79         : null,
     80 
     81       !session.user ? null : vnode.state.creating
     82         ? (
     83           <form class="panel" onsubmit={create}>
     84             {field('id', 'id')}
     85             {field('name', 'name')}
     86             {field('repo_url', 'repository url', true)}
     87             {field('default_branch', 'default branch')}
     88             <div class="actions">
     89               <button type="submit">create</button>
     90               <button type="button" onclick={() => { vnode.state.creating = false; }}>cancel</button>
     91             </div>
     92           </form>
     93         )
     94         : <div class="actions"><button onclick={() => { vnode.state.creating = true; }}>new project</button></div>,
     95 
     96       rows.rows.length === 0 ? <Empty what="projects" /> : (
     97         <table>
     98           <thead>
     99             <tr><th>project</th><th>repository</th><th>branch</th><th>visibility</th><th>jobs</th><th>created</th></tr>
    100           </thead>
    101           <tbody>
    102             {rows.rows.map((p) => (
    103               <tr key={p.id} class={p.enabled ? '' : 'disabled'}>
    104                 <td><m.route.Link href={`/projects/${p.id}`}>{p.name}</m.route.Link></td>
    105                 <td class="mono ellipsis">{p.repo_url}</td>
    106                 <td class="ref">{p.default_branch}</td>
    107                 <td>{p.visibility}</td>
    108                 <td>{p.job_counter}</td>
    109                 <td><When at={p.created_at} /></td>
    110               </tr>
    111             ))}
    112           </tbody>
    113         </table>
    114       ),
    115 
    116       <Pager
    117         offset={rows.offset}
    118         total={rows.total}
    119         limit={LIMIT}
    120         onmove={(offset: number) => { vnode.state.offset = offset; vnode.state.load(); }}
    121       />,
    122     ];
    123   },
    124 };