import type { Project } from '@lib/schema';

/* JobTrack case study. Source: D:\Job tracker + current app mockups. */
export const jobtrack: Project = {
  id: 'jobtrack',
  name: 'JobTrack',
  order: 1,
  status: 'in-development',
  template: 'case-study',
  stack: [
    { name: 'ASP.NET Core', context: { en: '.NET API', no: '.NET-API' } },
    { name: 'React', context: { en: 'TypeScript SPA', no: 'TypeScript-SPA' } },
    { name: 'EF Core' },
    { name: 'FastAPI', context: { en: 'local AI service', no: 'lokal AI-tjeneste' } },
    { name: 'Gmail OAuth' },
    { name: 'Docker' },
  ],
  links: [],
  diagram: {
    viewBox: '0 0 1120 300',
    title: { en: 'JobTrack architecture', no: 'JobTrack-arkitektur' },
    desc: {
      en: 'A React single-page app talks to an ASP.NET Core API, which persists via EF Core with attachments on disk, calls a FastAPI/Ollama AI service, and imports from the external Gmail API over OAuth2.',
      no: 'En React-app snakker med et ASP.NET Core-API som lagrer via EF Core med vedlegg på disk, kaller en FastAPI/Ollama AI-tjeneste, og importerer fra det eksterne Gmail-API-et via OAuth2.',
    },
    nodes: [
      {
        id: 'react',
        x: 40,
        y: 112,
        w: 180,
        h: 72,
        kind: 'internal',
        label: 'React SPA',
        sub: { en: 'nginx · PWA', no: 'nginx · PWA' },
      },
      {
        id: 'api',
        x: 300,
        y: 112,
        w: 200,
        h: 72,
        kind: 'primary',
        label: 'ASP.NET Core API',
        sub: { en: 'auth · rules · CV match', no: 'auth · regler · CV-match' },
      },
      {
        id: 'data',
        x: 600,
        y: 44,
        w: 180,
        h: 64,
        kind: 'internal',
        label: 'EF Core + files',
        sub: { en: 'data · attachments', no: 'data · vedlegg' },
      },
      {
        id: 'ai',
        x: 600,
        y: 156,
        w: 180,
        h: 64,
        kind: 'internal',
        label: 'AI service',
        sub: { en: 'FastAPI → Ollama', no: 'FastAPI → Ollama' },
      },
      {
        id: 'gmail',
        x: 900,
        y: 112,
        w: 180,
        h: 72,
        kind: 'external',
        label: 'Gmail API',
        sub: { en: 'external · OAuth2', no: 'eksternt · OAuth2' },
      },
    ],
    edges: [
      { d: 'M220 148 H300', kind: 'flow', label: '/api', labelX: 248, labelY: 140 },
      { d: 'M500 130 L600 88', kind: 'flow' },
      { d: 'M500 166 L600 200', kind: 'flow' },
      { d: 'M500 148 H900', kind: 'external', label: 'import', labelX: 690, labelY: 140 },
    ],
  },
  media: [
    {
      src: '/assets/projects/jobtrack/workspace.webp',
      width: 1600,
      height: 1000,
      alt: {
        en: 'JobTrack job workspace: one application with its details, CV match and recruiter thread',
        no: 'JobTrack jobb-arbeidsrom: én søknad med detaljer, CV-match og rekruttør-tråd',
      },
      caption: {
        en: 'Job workspace — everything about one application in one place',
        no: 'Jobb-arbeidsrom — alt om én søknad på ett sted',
      },
    },
    {
      src: '/assets/projects/jobtrack/pipeline.webp',
      width: 1600,
      height: 1000,
      alt: {
        en: 'JobTrack Kanban pipeline with applications across Applied, Waiting, Interview and Offer',
        no: 'JobTrack Kanban-pipeline med søknader fordelt på stadiene',
      },
      caption: {
        en: 'Pipeline — drag applications between stages, from Applied to Offer',
        no: 'Pipeline — dra søknader mellom stadier, fra Søkt til Tilbud',
      },
    },
    {
      src: '/assets/projects/jobtrack/dashboard.webp',
      width: 1600,
      height: 1000,
      alt: {
        en: 'JobTrack dashboard with response rate, funnel and time-in-stage',
        no: 'JobTrack-dashbord med svarrate, trakt og tid-per-stadium',
      },
      caption: {
        en: 'Dashboard — response rates, funnel and skill demand across the search',
        no: 'Dashbord — svarrater, trakt og etterspurte ferdigheter',
      },
    },
  ],
  content: {
    en: {
      valueProp:
        'An AI-assisted job-search workspace — import a role, tailor your CV, and track every application from applied to offer.',
      cardTeaser:
        'Import a role, tailor your CV, and track every application — recruiter threads and a Kanban pipeline in one workspace.',
      tldr: {
        what: 'Full-stack job-search workspace: pipeline, CV match, Gmail threads, attachments, analytics.',
        why: 'A real problem — my own job search needed production-grade tooling, not a spreadsheet.',
        stack: 'React + ASP.NET Core (EF Core) · FastAPI/Ollama local AI · Gmail OAuth · Docker.',
        role: 'Everything: product, backend, frontend, ops and security.',
      },
      sections: [
        {
          kind: 'problem',
          heading: 'Problem & context',
          anchorId: 'problem',
          blocks: [
            {
              type: 'p',
              text: 'A serious job search spreads across spreadsheets, email threads, notes apps and scattered documents. Nothing shows you, at a glance, which applications need attention or what was said last. I wanted one focused workspace — from importing a role to the final offer — built to the standard I would ship at work, not as a throwaway.',
            },
          ],
        },
        {
          kind: 'architecture',
          heading: 'Architecture',
          anchorId: 'architecture',
          blocks: [
            {
              type: 'p',
              text: 'A React + TypeScript SPA talks to an ASP.NET Core API that owns the domain logic — pipeline, follow-up rules, CV keyword matching — persisting via EF Core with attachments on disk. A small FastAPI service backed by a local Ollama model drafts CVs, cover letters and follow-ups, and the API imports correspondence from the Gmail API over OAuth2. The whole thing runs behind one Docker Compose file.',
            },
            {
              type: 'ul',
              items: [
                'Pipeline: a Kanban board — Applied, Waiting, Interview, Offer, Rejected, Ghosted — drag to update.',
                'CV match: deterministic keyword coverage (matched vs missing), not a black-box score.',
                'Gmail: import full threads over OAuth2; linked threads auto-refresh onto the right job.',
                'AI is assistive, never autonomous: it drafts, you always review and send — no auto-apply.',
              ],
            },
          ],
        },
        {
          kind: 'decisions',
          heading: 'Key decisions & trade-offs',
          anchorId: 'decisions',
          decisions: [
            {
              n: 1,
              choice: 'Run the AI locally with Ollama instead of a cloud API.',
              alternative: 'A hosted LLM API would have been faster to wire up.',
              rationale:
                'Job-search data is sensitive and I wanted zero per-call cost and no third party in the loop. The trade-off is more setup and heavier local resources — acceptable for a self-hosted tool.',
            },
            {
              n: 2,
              choice: 'Make CV matching deterministic, not an AI score.',
              alternative: 'Let the model rate the fit.',
              rationale:
                'A number a candidate can’t interrogate is useless. Deterministic keyword coverage shows exactly which terms matched and which are missing, so the advice is honest and actionable.',
            },
            {
              n: 3,
              choice: 'Ship the PWA with no offline service-worker cache.',
              alternative: 'A cache would enable full offline use.',
              rationale:
                'I deploy frequently, so an aggressive cache risks serving stale builds — a worse failure than a brief offline gap. The manifest still provides installability and share-to-capture. A deliberate anti-feature.',
            },
          ],
        },
        {
          kind: 'security',
          heading: 'Security & production notes',
          anchorId: 'security',
          blocks: [
            {
              type: 'ul',
              items: [
                'Optional Google sign-in (Google ID tokens) protects the API; every record is scoped to its owner.',
                'File uploads are validated and stored per-application with ownership checks on every access.',
                'The AI layer is advisory only — it drafts, it never sends or auto-applies.',
                'Runs as a reproducible Docker Compose stack with a documented .env; JSON/CSV exports for data portability.',
              ],
            },
          ],
        },
        {
          kind: 'screenshots',
          heading: 'Screenshots',
          anchorId: 'screenshots',
        },
        {
          kind: 'next',
          heading: 'Status & what’s next',
          anchorId: 'next',
          blocks: [
            {
              type: 'p',
              text: 'In active development. The follow-up rules are simple date logic I’d like to make configurable per stage; next up is a proper integration-test pass around the Gmail import edge cases and tighter grounding on the AI drafts now that I have real usage to learn from.',
            },
          ],
        },
      ],
    },
    no: {
      valueProp:
        'Et AI-assistert arbeidsrom for jobbsøking — importer en stilling, tilpass CV-en, og følg hver søknad fra søkt til tilbud.',
      cardTeaser:
        'Importer en stilling, tilpass CV-en, og følg hver søknad — rekruttør-tråder og en Kanban-pipeline i ett arbeidsrom.',
      tldr: {
        what: 'Fullstack arbeidsrom for jobbsøking: pipeline, CV-match, Gmail-tråder, vedlegg og analyse.',
        why: 'Et ekte problem — min egen jobbsøking trengte skikkelig verktøy, ikke et regneark.',
        stack: 'React + ASP.NET Core (EF Core) · FastAPI/Ollama lokal AI · Gmail OAuth · Docker.',
        role: 'Alt: produkt, backend, frontend, drift og sikkerhet.',
      },
      sections: [
        {
          kind: 'problem',
          heading: 'Problem og kontekst',
          anchorId: 'problem',
          blocks: [
            {
              type: 'p',
              text: 'En seriøs jobbsøking sprer seg over regneark, e-posttråder, notatapper og løse dokumenter. Ingenting viser deg med ett blikk hvilke søknader som trenger oppfølging, eller hva som sist ble sagt. Jeg ville ha ett samlet arbeidsrom — fra import av en stilling til endelig tilbud — bygget til samme standard som jeg leverer på jobb.',
            },
          ],
        },
        {
          kind: 'architecture',
          heading: 'Arkitektur',
          anchorId: 'arkitektur',
          blocks: [
            {
              type: 'p',
              text: 'En React + TypeScript-SPA snakker med et ASP.NET Core-API som eier domenelogikken — pipeline, oppfølgingsregler, CV-nøkkelordmatch — og lagrer via EF Core med vedlegg på disk. En liten FastAPI-tjeneste med en lokal Ollama-modell skriver utkast til CV, søknadsbrev og oppfølging, og API-et importerer korrespondanse fra Gmail-API-et via OAuth2. Alt kjører bak én Docker Compose-fil.',
            },
            {
              type: 'ul',
              items: [
                'Pipeline: et Kanban-brett — Søkt, Venter, Intervju, Tilbud, Avslått, Ghostet — dra for å oppdatere.',
                'CV-match: deterministisk nøkkelorddekning (treff vs. mangler), ikke en svart boks.',
                'Gmail: importer hele tråder via OAuth2; koblede tråder oppdateres automatisk på riktig jobb.',
                'AI-en er assisterende, aldri autonom: den skriver utkast, du gjennomgår og sender — ingen auto-søking.',
              ],
            },
          ],
        },
        {
          kind: 'decisions',
          heading: 'Viktige valg og avveininger',
          anchorId: 'beslutninger',
          decisions: [
            {
              n: 1,
              choice: 'Kjøre AI-en lokalt med Ollama i stedet for et sky-API.',
              alternative: 'Et hostet LLM-API hadde vært raskere å koble til.',
              rationale:
                'Jobbsøkerdata er sensitivt, og jeg ville ha null kostnad per kall og ingen tredjepart i loopen. Avveiningen er mer oppsett og tyngre lokale ressurser — greit for et egendriftet verktøy.',
            },
            {
              n: 2,
              choice: 'Gjøre CV-matchen deterministisk, ikke en AI-score.',
              alternative: 'La modellen vurdere treffet.',
              rationale:
                'Et tall du ikke kan etterprøve er ubrukelig. Deterministisk nøkkelorddekning viser nøyaktig hvilke ord som traff og hvilke som mangler, så rådet er ærlig og handlingsrettet.',
            },
            {
              n: 3,
              choice: 'Levere PWA-en uten offline-cache i service-workeren.',
              alternative: 'En cache ville gitt full offline-bruk.',
              rationale:
                'Jeg ruller ut ofte, så en aggressiv cache risikerer å servere utdaterte bygg — en verre feil enn et kort offline-hull. Manifestet gir fortsatt installerbarhet og share-to-capture. En bevisst anti-funksjon.',
            },
          ],
        },
        {
          kind: 'security',
          heading: 'Sikkerhet og drift',
          anchorId: 'sikkerhet',
          blocks: [
            {
              type: 'ul',
              items: [
                'Valgfri Google-innlogging (Google ID-tokens) beskytter API-et; hver post er knyttet til sin eier.',
                'Filopplastinger valideres og lagres per søknad, med eierskapssjekk ved hvert tilgangspunkt.',
                'AI-laget er kun rådgivende — det skriver utkast, det sender aldri og søker aldri automatisk.',
                'Kjører som en reproduserbar Docker Compose-stack med dokumentert .env; JSON/CSV-eksport for dataportabilitet.',
              ],
            },
          ],
        },
        {
          kind: 'screenshots',
          heading: 'Skjermbilder',
          anchorId: 'skjermbilder',
        },
        {
          kind: 'next',
          heading: 'Status og veien videre',
          anchorId: 'videre',
          blocks: [
            {
              type: 'p',
              text: 'Under aktiv utvikling. Oppfølgingsreglene er enkel datologikk jeg vil gjøre konfigurerbar per stadium; neste steg er skikkelige integrasjonstester rundt kanttilfellene i Gmail-importen og strammere forankring av AI-utkastene nå som jeg har ekte bruk å lære av.',
            },
          ],
        },
      ],
    },
  },
};
