diff --git a/.gitea/workflows/ci-deploy.yml b/.gitea/workflows/ci-deploy.yml index 045ace2..3254d03 100644 --- a/.gitea/workflows/ci-deploy.yml +++ b/.gitea/workflows/ci-deploy.yml @@ -76,12 +76,17 @@ jobs: APP_BUILD_STAMP="$(date -u +'%Y-%m-%d %H:%M UTC')" \ ./deploy/deploy.sh docker compose ps - docker compose exec -T ai-service python -c "import time, urllib.request; deadline=time.time()+60; last=None -for _ in range(30): - try: - urllib.request.urlopen('http://127.0.0.1:8001/health', timeout=5).read() - raise SystemExit(0) - except Exception as exc: - last=exc - time.sleep(2) -raise last" + docker compose exec -T ai-service python - <<'PY' + import time + import urllib.request + + last = None + for _ in range(30): + try: + urllib.request.urlopen('http://127.0.0.1:8001/health', timeout=5).read() + raise SystemExit(0) + except Exception as exc: + last = exc + time.sleep(2) + raise last + PY diff --git a/job-tracker-ui/src/i18n/translations.ts b/job-tracker-ui/src/i18n/translations.ts index b44b5ea..2c2e40f 100644 --- a/job-tracker-ui/src/i18n/translations.ts +++ b/job-tracker-ui/src/i18n/translations.ts @@ -211,6 +211,7 @@ export const translations = { profileCvSectionDraft: "Section draft", profileCvSectionDraftPlaceholder: "Your rewritten section will appear here.", profileCvSectionAppend: "Append to CV text", + profileCvSectionReplace: "Replace matching section", profileSaveChanges: "Save changes", profileUpdated: "Profile updated.", profileUpdateFailed: "Failed to update profile.", @@ -984,6 +985,7 @@ export const translations = { profileCvSectionDraft: "Seksjonsutkast", profileCvSectionDraftPlaceholder: "Den omskrevne seksjonen vises her.", profileCvSectionAppend: "Legg til i CV-teksten", + profileCvSectionReplace: "Erstatt matchende seksjon", profileSaveChanges: "Lagre endringer", profileUpdated: "Profil oppdatert.", profileUpdateFailed: "Kunne ikke oppdatere profil.", diff --git a/job-tracker-ui/src/pages/ProfilePage.tsx b/job-tracker-ui/src/pages/ProfilePage.tsx index 448251e..7b79bf4 100644 --- a/job-tracker-ui/src/pages/ProfilePage.tsx +++ b/job-tracker-ui/src/pages/ProfilePage.tsx @@ -46,6 +46,28 @@ function initialsFrom(values: Array) { return (joined[0][0] + joined[1][0]).toUpperCase(); } +function replaceCvSection(source: string, sectionName: string, sectionDraft: string) { + const trimmedSource = source.trim(); + const trimmedDraft = sectionDraft.trim(); + if (!trimmedDraft) return source; + if (!trimmedSource) return `${sectionName}\n${trimmedDraft}`; + + const headingPattern = /^([A-Z][A-Za-z &/]+):?\s*$/gm; + const matches = Array.from(trimmedSource.matchAll(headingPattern)); + const normalizedTarget = sectionName.trim().toLowerCase(); + const targetIndex = matches.findIndex((match) => match[1].trim().toLowerCase() === normalizedTarget); + + if (targetIndex === -1) { + return `${trimmedSource}\n\n${sectionName}\n${trimmedDraft}`.trim(); + } + + const start = matches[targetIndex].index ?? 0; + const end = targetIndex + 1 < matches.length ? (matches[targetIndex + 1].index ?? trimmedSource.length) : trimmedSource.length; + const before = trimmedSource.slice(0, start).trimEnd(); + const after = trimmedSource.slice(end).trimStart(); + return [before, `${sectionName}\n${trimmedDraft}`, after].filter(Boolean).join("\n\n").trim(); +} + export default function ProfilePage() { const { toast } = useToast(); const { t } = useI18n(); @@ -376,6 +398,7 @@ export default function ProfilePage() { +