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

/* JobTrack case study. Source: D:\Job tracker (README + website_details.md). */
export const jobtrack: Project = {
  id: 'jobtrack',
  name: 'JobTrack',
  order: 1,
  status: 'active',
  template: 'case-study',
  stack: [
    { name: '.NET 9', context: { en: 'ASP.NET Core API', no: 'ASP.NET Core-API' } },
    { name: 'React', context: { en: 'SPA frontend', no: 'SPA-frontend' } },
    { name: 'TypeScript' },
    { name: 'SQLite', context: { en: 'EF Core', no: 'EF Core' } },
    { name: 'FastAPI', context: { en: 'Python AI service', no: 'Python AI-tjeneste' } },
    { name: 'Ollama', context: { en: 'local LLM', no: 'lokal LLM' } },
    { 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 to SQLite and 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 til SQLite og 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: '.NET 9 · auth · rules', no: '.NET 9 · auth · regler' },
      },
      {
        id: 'data',
        x: 600,
        y: 44,
        w: 180,
        h: 64,
        kind: 'internal',
        label: 'SQLite + files',
        sub: { en: 'EF Core · attachments', no: 'EF Core · 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: 'placeholder:jobtrack-table',
      width: 1600,
      height: 1000,
      alt: {
        en: 'JobTrack applications table with status filters and search',
        no: 'JobTrack-søknadstabell med statusfiltre og søk',
      },
      caption: {
        en: 'Applications table — pipeline at a glance',
        no: 'Søknadstabell — hele pipelinen samlet',
      },
    },
    {
      src: 'placeholder:jobtrack-details',
      width: 1600,
      height: 1000,
      alt: {
        en: 'Job details dialog combining summary, notes, correspondence and attachments',
        no: 'Jobbdetaljer med sammendrag, notater, korrespondanse og vedlegg',
      },
      caption: {
        en: 'Job details — everything about one application in one place',
        no: 'Jobbdetaljer — alt om én søknad på ett sted',
      },
    },
    {
      src: 'placeholder:jobtrack-gmail',
      width: 1600,
      height: 1000,
      alt: {
        en: 'Gmail import view with smart query filters and thread import',
        no: 'Gmail-import med smarte søkefiltre og trådimport',
      },
      caption: {
        en: 'Gmail import — pull correspondence straight onto the application',
        no: 'Gmail-import — hent korrespondanse rett inn på søknaden',
      },
    },
  ],
  content: {
    en: {
      valueProp:
        'A job-application workspace covering the full journey from application to follow-up.',
      cardTeaser:
        'One workspace for the whole job-application journey — pipeline, correspondence, attachments, AI summaries.',
      tldr: {
        what: 'Full-stack job-application tracker: pipeline, correspondence, attachments, analytics.',
        why: 'A real problem — my own job search needed production-grade tooling, not a spreadsheet.',
        stack: 'React + ASP.NET Core + SQLite · FastAPI/Ollama AI service · Docker Compose.',
        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 — and I wanted to build it to the standard I would ship at work, not as a throwaway.',
            },
          ],
        },
        {
          kind: 'architecture',
          heading: 'Architecture',
          anchorId: 'architecture',
          blocks: [
            {
              type: 'p',
              text: 'Four services behind one Docker Compose file. A React SPA talks to an ASP.NET Core (.NET 9) API that owns the domain logic and follow-up rules, persisting to SQLite via EF Core with attachments on disk. A small FastAPI service backed by a local Ollama model produces summaries and skill extraction, and the API imports correspondence from the Gmail API over OAuth2.',
            },
            {
              type: 'ul',
              items: [
                'Frontend: React + TypeScript SPA, served by nginx, installable as a PWA with a mobile share-target for quick capture.',
                'API: ASP.NET Core, EF Core, rule-driven follow-up reminders, per-application history trail.',
                'AI: FastAPI wrapper over Ollama for short/full description summaries — advisory only.',
                'Integration: Gmail OAuth2 with smart query filters and single-message or full-thread import.',
              ],
            },
          ],
        },
        {
          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, single-user tool.',
            },
            {
              n: 2,
              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.',
            },
            {
              n: 3,
              choice: 'SQLite rather than PostgreSQL.',
              alternative: 'Postgres is the default reach for a web backend.',
              rationale:
                'This is a single-user app; SQLite removes an entire service with no practical downside here. If it ever went multi-tenant, moving to Postgres via EF Core is a contained change.',
            },
          ],
        },
        {
          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 never performs destructive actions.',
                'Runs as a reproducible Docker Compose stack with a documented .env; scheduled JSON/CSV exports for data portability.',
              ],
            },
          ],
        },
        {
          kind: 'screenshots',
          heading: 'Screenshots',
          anchorId: 'screenshots',
        },
        {
          kind: 'next',
          heading: 'What I’d improve next',
          anchorId: 'next',
          blocks: [
            {
              type: 'p',
              text: 'The rules engine for follow-ups is simple date logic; I’d make it configurable per pipeline stage. I’d also add a proper integration-test pass around the Gmail import edge cases, and tighten the summariser prompts now that I have real usage to learn from.',
            },
          ],
        },
      ],
    },
    no: {
      valueProp: 'Et arbeidsrom for jobbsøking som dekker hele reisen fra søknad til oppfølging.',
      cardTeaser:
        'Ett arbeidsrom for hele jobbsøkerreisen — pipeline, korrespondanse, vedlegg og AI-sammendrag.',
      tldr: {
        what: 'Fullstack jobbsøknads-tracker: pipeline, korrespondanse, vedlegg og analyse.',
        why: 'Et ekte problem — min egen jobbsøking trengte skikkelig verktøy, ikke et regneark.',
        stack: 'React + ASP.NET Core + SQLite · FastAPI/Ollama AI-tjeneste · Docker Compose.',
        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 — og bygge det til samme standard som jeg leverer på jobb, ikke som noe engangs.',
            },
          ],
        },
        {
          kind: 'architecture',
          heading: 'Arkitektur',
          anchorId: 'arkitektur',
          blocks: [
            {
              type: 'p',
              text: 'Fire tjenester bak én Docker Compose-fil. En React-app snakker med et ASP.NET Core-API (.NET 9) som eier domenelogikken og oppfølgingsreglene, og lagrer til SQLite via EF Core med vedlegg på disk. En liten FastAPI-tjeneste med en lokal Ollama-modell lager sammendrag og trekker ut ferdigheter, og API-et importerer korrespondanse fra Gmail-API-et via OAuth2.',
            },
            {
              type: 'ul',
              items: [
                'Frontend: React + TypeScript, servert av nginx, installerbar som PWA med share-target for rask registrering.',
                'API: ASP.NET Core, EF Core, regelstyrte oppfølgingspåminnelser og historikk per søknad.',
                'AI: FastAPI over Ollama for korte og fulle sammendrag — kun rådgivende.',
                'Integrasjon: Gmail OAuth2 med smarte søkefiltre og import av enkeltmeldinger eller hele tråder.',
              ],
            },
          ],
        },
        {
          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 med én bruker.',
            },
            {
              n: 2,
              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.',
            },
            {
              n: 3,
              choice: 'SQLite framfor PostgreSQL.',
              alternative: 'Postgres er standardvalget for en web-backend.',
              rationale:
                'Dette er en app for én bruker; SQLite fjerner en hel tjeneste uten praktisk ulempe her. Skulle den bli fler-bruker, er overgang til Postgres via EF Core en avgrenset endring.',
            },
          ],
        },
        {
          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 utfører aldri destruktive handlinger.',
                'Kjører som en reproduserbar Docker Compose-stack med dokumentert .env; planlagte JSON/CSV-eksporter for dataportabilitet.',
              ],
            },
          ],
        },
        {
          kind: 'screenshots',
          heading: 'Skjermbilder',
          anchorId: 'skjermbilder',
        },
        {
          kind: 'next',
          heading: 'Hva jeg ville forbedret',
          anchorId: 'videre',
          blocks: [
            {
              type: 'p',
              text: 'Regelmotoren for oppfølging er enkel datologikk; jeg ville gjort den konfigurerbar per pipeline-steg. Jeg ville også lagt til skikkelige integrasjonstester rundt kanttilfellene i Gmail-importen, og strammet inn sammendrags-promptene nå som jeg har ekte bruk å lære av.',
            },
          ],
        },
      ],
    },
  },
};
