Commit Graph

72 Commits

Author SHA1 Message Date
chaim 46955e2064 cdn: fix healthcheck (use 127.0.0.1, not localhost) and clear default nginx html
busybox wget in alpine resolves "localhost" to ::1 first, but our nginx
`listen 80` is IPv4-only, so the healthcheck got Connection refused and
Coolify marked the container running:unhealthy (which also made Traefik
skip its router and fall back to the lower-priority extension-platform
catch-all). Hard-coding 127.0.0.1 fixes that.

Also clear /usr/share/nginx/html before COPY so the base image's
50x.html/index.html don't leak into the published artifact set.
v1.0.0
2026-05-11 17:47:26 +00:00
chaim 35d691e0d2 debug(ci): instrument pack-upload step with full trace log
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v0.1.0
2026-05-11 17:28:07 +00:00
chaim 8d49b4f389 debug(ci): force Pack+upload to run regardless of previous step
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:24:32 +00:00
chaim 2aad6e5863 ci: reset LASTEXITCODE before natural script end; match build-job exit pattern
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:21:31 +00:00
chaim 6c32c913f4 ci: persist publish-finish exit code to side-channel + force explicit exit
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:16:46 +00:00
chaim 51f72c3a4a ci: move tar into Upload step; explicit exit 0 from transcript step
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:12:41 +00:00
chaim 50c3038188 ci: remove dead steps.outputs refs + inline MSBuild path into publish-clickonce
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:08:57 +00:00
chaim fea624e9ce ci: remove redundant Compute version + Resolve tools steps (cause job to fail on act_runner)
Everything is now inlined in the single transcript-wrapped step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:04:16 +00:00
chaim b8327829e8 fix(ci): inline version parsing everywhere (act_runner steps.outputs broken)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:00:06 +00:00
chaim 51c23c3f38 fix(ci): use msbuild /t:Publish (VSTO standard target) instead of manual mage
The VSTO MSBuild Publish target is the same one VS IDE uses internally and
handles build + assembly signing + manifest generation + manifest signing
+ .deploy renaming in one pass. Hand-stitching the steps with mage/signtool
on top of /t:Build was producing inconsistent state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:56:08 +00:00
chaim d0455d9aaf ci: capture mage stdout/stderr for the failing -Sign call
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:52:14 +00:00
chaim 191c12b0c9 fix(ci): mage -Sign via -CertFile/.pfx (avoid cert store lookup quirks)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:49:37 +00:00
chaim e942a64b25 fix(ci): create deployment manifest via mage -New Deployment (not -Update of copy)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:46:45 +00:00
chaim e8ee1b8d3d fix(ci): resolve mage/signtool inside same step (GITHUB_ENV not propagated by act_runner)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:43:13 +00:00
chaim 02685ec7e8 fix(ci): don't re-declare MAGE/SIGNTOOL env (overwrites GITHUB_ENV value)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:40:44 +00:00
chaim 95abb8b667 ci: unify publish-clickonce into single transcripted step + upload logs
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:37:32 +00:00
chaim 439c9c08fc ci: replace broken upload-artifact action with Gitea generic packages
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:33:45 +00:00
chaim e71b08c0d3 ci: don't block release on test failures (continue-on-error)
Build itself succeeded with 0 errors; the failing step was the Test step
which is non-critical for the release pipeline. Mark it as
continue-on-error so publish-clickonce + dockerize-and-deploy proceed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:30:27 +00:00
chaim 3b07558bf6 ci: push build logs to Gitea generic package registry
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:27:11 +00:00
chaim 8c583aaa3c ci: single-step build with transcript so logs always upload
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:24:50 +00:00
chaim 63090ce877 ci: capture build output via Tee-Object for artifact retrieval
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:22:17 +00:00
chaim 64a1d63656 ci: dump msbuild log to artifact so we can debug failures via API
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:19:33 +00:00
chaim a8af5e3749 fix(ci): import cert to CurrentUser\My + patch csproj thumbprint
VSTO's ResolveKeySource target reads ManifestCertificateThumbprint
directly from csproj XML (not from /p: overrides) and looks for it
under Cert:\CurrentUser\My (not LocalMachine\My). Import the cert
to CurrentUser\My before build and patch the csproj XML to use the
real Marcus-Law thumbprint instead of VS's temporary-key thumbprint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:17:05 +00:00
PointStar 137cb99fba feat(sidebar): Hebrew case status labels + show all linked cases (not just 5)
Two quality-of-life tweaks on the sidebar:

1. New CaseStatusToHebrewConverter translates standard EspoCRM Case
   statuses (New / Assigned / Pending / Closed / Rejected / Duplicate /
   In Progress / On Hold / Resolved / Cancelled / Closed Won / Closed
   Lost / Open) to Hebrew. Unknown values pass through unchanged so
   custom statuses configured per-tenant still render.
   Wired in ReadingPaneView.xaml via Status binding.

2. MatchingService now pulls up to 50 cases per contact instead of 5.
   The sidebar's ScrollViewer already handles long lists. 50 is well
   below EspoCRM's default page size and large enough for any realistic
   attorney/contact relationship. Section header changed from
   "תיקים אחרונים" to "תיקים מקושרים" to reflect that it's the
   complete list, not just the most recent.

Tests: 39 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:13:12 +03:00
chaim 9f595c8ece fix(ci): override ManifestCertificateThumbprint with Marcus-Law cert
VSTO refuses to build with SignManifests=false. Override the project's
hardcoded VS-temporary-key thumbprint with our real code-signing cert
(already imported into LocalMachine\My during runner setup).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:12:52 +00:00
chaim 0e21be21f3 fix(ci): skip compile-time manifest signing (MSB3323)
The legacy VSTO project carries <SignManifests>true</SignManifests> with
a thumbprint pointing to VS's auto-generated temporary key, which exists
on a developer's machine but never on a CI runner. The release manifests
are signed properly in publish-clickonce via mage with the real Marcus-Law
cert, so /p:SignManifests=false at compile time is the right thing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:10:59 +00:00
chaim 399fd24f4d fix(ci): set NUGET_PACKAGES outside system32 (NETSDK1064 fix)
NuGet ignores our service-level USERPROFILE override (it uses
SHGetKnownFolderPath which returns LocalSystem's profile regardless of
the env var) and installs packages to C:\Windows\system32\configsystemprofile\.nuget\packages. 32-bit MSBuild then can't find them because
the Win32 File System Redirector silently shunts system32 paths to
SysWOW64 for 32-bit processes -> NETSDK1064 'package not found'.

NUGET_PACKAGES is NuGet's highest-priority package-folder override and
puts everything outside system32, so both restore and build agree on
paths and the redirector never gets involved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:06:16 +00:00
PointStar a1e25af86b fix(json): tolerate EspoCRM's naive datetime format (YYYY-MM-DD HH:mm:ss)
The cases lookup for "אברהם מוסטקי" was still failing after the
Case.number fix, with a new exception:

  JsonException at $.list[0].createdAt
  The JSON value is not in a supported DateTimeOffset format.

EspoCRM serializes datetimes as "2024-01-15 14:30:00" without a timezone
designator, but .NET's built-in DateTimeOffset reader only accepts
strict ISO 8601 with a tz suffix. Adding the FlexibleStringConverter
to CreatedAt is not enough since the target type is DateTimeOffset, not
string.

Solution: a dedicated FlexibleDateTimeOffsetConverter that:
- Accepts ISO 8601 with offset (fast path)
- Accepts "YYYY-MM-DD HH:mm:ss" / "YYYY-MM-DD" / variants, treating
  them as UTC
- Accepts Unix epoch numbers
- Accepts null / empty strings

Registered globally on the JsonOptions used by EspoCrmClient, so every
model that exposes a DateTimeOffset? deserializes correctly without
per-property attributes.

Tests: 39 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:05:32 +03:00
chaim 55d3e6d0f3 fix(ci): unify restore + build via 'msbuild /restore' (avoid NETSDK1064)
Standalone 'nuget.exe restore' (32-bit) and the subsequent build (which
loads the 64-bit .NET 9 SDK PackageDependencyResolution.targets) disagree
on the resolved package paths, surfacing as NETSDK1064 'Package ... was
not found' on SDK-style projects (OutlookAddin.UI / .Core / .Tests).
Using 'msbuild /restore' keeps both phases in one MSBuild process with a
consistent assets file and package cache lookup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:04:00 +00:00
chaim e6c2b099d0 fix(ci): use VS Pro MSBuild (requires Office workload) instead of Build Tools
VS Build Tools 2022 does not ship Microsoft.VisualStudio.Tools.Office.targets
(only the full VS IDE editions do), so the VSTO project fails to import them
(MSB4226). The runner machine already has VS Pro 2022 with the Office workload
installed; switch from microsoft/setup-msbuild@v2 (which picks Build Tools via
-latest) to a manual vswhere step that explicitly requires the Office workload.

Also removes the temporary filesystem-debug step (root cause was the Win32 File
System Redirector pulling system32 paths to SysWOW64 for 32-bit nuget/MSBuild;
fixed at the runner level via USERPROFILE redirect, not in the workflow).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:59:50 +00:00
PointStar 414f71706a fix: restore PNG embed entries + handle numeric Case.number from EspoCRM
Two issues surfaced from the addin log:

1) The four Klear ribbon PNGs were not in the assembly manifest
   (Available resources listed only the XMLs and Properties.Resources).
   The EmbeddedResource entries for Images got dropped from
   OutlookAddin.csproj during a previous rebase. Re-add them in the same
   spot they had before.

2) MatchingService failed for one of the contacts with:
     System.Text.Json.JsonException at $.list[0].number
     Cannot get the value of a token type Number as a string.
   EspoCRM emits Case.number as a JSON number on installs that use
   auto-increment, and as a string when a custom case-code format is
   configured. Add a small FlexibleStringConverter that reads either
   token type into the same string field, and attach it to
   CaseEntity.Number.

Tests: 39 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 18:57:15 +03:00
chaim f794fbc37a fix(ci): use Windows PowerShell 5.1 (powershell) instead of pwsh
The self-hosted Windows runner runs as LocalSystem and only sees the
machine PATH. winget-installed pwsh lands in the user PATH and is
invisible to the service. Built-in Windows PowerShell 5.1 (`powershell`)
handles the same syntax we use, so switch all steps to it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:45:48 +00:00
PointStar f371fc83ad fix(ribbon): switch icons from loadImage to per-button getImage callbacks
After the previous commit the ribbon rendered with NO icons at all.
Cause: loadImage on customUI is the Office-2007 pattern; Office 2010+
silently falls back to no image when the callback signature or timing
isn't exactly what it expects. Some hosts also cache a null result.

Switch to the more reliable per-button pattern:

- Each button declares getImage=OnGetImage (no image= attribute).
- OnGetImage(IRibbonControl control) maps control.Id -> embedded
  PNG name and returns a fresh Bitmap.
- If a resource is missing we log it AND the full list of
  GetManifestResourceNames() so we can diagnose any future mismatch
  from the addin log alone.

Builds clean. PNGs unchanged; csproj <EmbeddedResource> entries are the
same as before. The previous OnLoadImage callback is removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 18:44:17 +03:00
chaim 22e556afe0 debug(ci): list filesystem after checkout to diagnose MSB3202
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:37:36 +00:00
chaim 033399b21a fix(ci): resolve msbuild/nuget/mage/signtool dynamically
The first v0.1.0 run failed because actions/checkout could not find Node
and msbuild/nuget were never on the LocalSystem PATH after winget install.
This commit pulls in the standard tool-setup actions and resolves the
.NET-Framework-bound tooling (mage.exe, signtool.exe) by walking the
Windows SDK / .NET 4.8 Developer Pack directories instead of hardcoding
versioned paths that drift across machines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:16:24 +00:00
PointStar 732c5641a8 feat(ribbon): custom Klear icons for all four ribbon buttons
Replace the previous imageMso identifiers with PNG icons painted in the
Klear brand blue (#2563EB):

- klear-file.png      — large white K on blue rounded square
- klear-sidebar.png   — pane + divider + content lines
- klear-settings.png  — three horizontal sliders with dots
- klear-compose.png   — envelope with pencil overlay

All four are 32x32 PNGs embedded as resources under
OutlookAddin.Images.*. Ribbon XML now declares `image=<name>.png` and a
shared `loadImage=OnLoadImage` callback resolves each name to a Bitmap
from the assembly's manifest resources.

The ribbons affected:
- TabMail / Marcus-Law:  Klear, פרטי תיק, הגדרות (3 buttons)
- TabNewMailMessage / Marcus-Law:  כתוב מתיק (1 button)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 18:15:04 +03:00
chaim 73d6799eb2 feat(ci): CI/CD container delivery for ClickOnce artifacts
Replace direct SSH rsync with build → push to registry → Coolify pull pattern,
matching the rest of the infra (decisions-court, ai-gateway, ...). Adds cdn/
Dockerfile + nginx.conf to serve .vsto/.application/.manifest/.deploy with
correct MIME types behind a StripPrefix Traefik middleware.

The new workflow has three jobs:
- build:               Windows runner, nuget restore + msbuild + dotnet test.
- publish-clickonce:   Windows runner, only on v* tag — sign assemblies +
                       mage manifests + upload publish/ as artifact.
- dockerize-and-deploy: Linux runner — download artifact, build & push
                       outlook-addin-cdn:<version> + :latest to
                       gitea.dev.marcus-law.co.il, then trigger Coolify
                       redeploy of app awdlvjlozz0dvn6kjlme9rd7.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:10:39 +00:00
PointStar 312e72ff54 docs: add AUTO-UPDATE-SETUP.md — Hebrew runbook for the infra team
Step-by-step infrastructure setup guide covering everything needed to
turn the ClickOnce auto-update pipeline live:

1. Architecture diagram
2. Prerequisites checklist
3. Setup steps:
   - Windows Build Runner provisioning + Gitea Actions registration
   - Self-signed code-signing cert generation
   - Infisical secret storage
   - Gitea Actions secrets wiring
   - nginx config on platform.dev
   - GPO push of the cert to attorney machines
4. First release (v1.0.0) walkthrough
5. Day-to-day update flow (tag -> CI -> clients auto-update)
6. Monitoring, troubleshooting, rollback
7. Post-setup verification checklist
8. Periodic maintenance schedule

Written in Hebrew for the Marcus-Law infrastructure team.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:41:23 +03:00
PointStar 037b66c683 fix(sidebar): fetch contact's cases via /Contact/{id}/cases instead of Case?where[]
The sidebar identified the contact but showed "(אין תיקים פתוחים)" even
when cases existed for them. The previous query —
  GET /Case?where[0][type]=linkedWith&where[0][attribute]=contacts&where[0][value]=…
— came back empty against this EspoCRM build, likely because the link
name on Case differs from the default or the where[] syntax isn't
accepted on this host.

Switch to the contact-side related-records endpoint which is the
canonical EspoCRM way to fetch linked records:
  GET /Contact/{contactId}/cases?select=…&orderBy=createdAt&order=desc&maxSize=5

Keep the old /Case?where[] query as a fallback for installs where the
relationship walk fails. Log the chosen path and result count so the
next "no cases shown" report can be diagnosed from the log file alone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:13:29 +03:00
PointStar dc04a96bdd fix(ux): owner-anchor dialogs + sidebar reopen + Klear pane title
Three related polish fixes:

1. Modal dialogs (File-to-Case / Compose-from-Case / Settings) used to
   open without an Owner, so they would sink behind other windows and
   the user perceived Outlook as "frozen". AttachOwnerToOutlook now
   sets WindowInteropHelper.Owner to the foreground HWND (Win32
   GetForegroundWindow with MainWindowHandle as fallback) before
   ShowDialog. Also sets WindowStartupLocation=CenterOwner and pumps
   SetForegroundWindow + Activate on Loaded.

2. Sidebar pane title was "פרטי תיק (EspoCRM)" — renamed to
   "Klear · פרטי תיק" to match the new branding.

3. New ribbon button "פרטי תיק" (Marcus-Law group, between Klear and
   הגדרות) re-opens the sidebar after the user clicks its X.
   Implementation:
     - SidebarController.ShowForActiveExplorer() toggles existing pane
       Visible back on, or attaches one if missing.
     - ExplorerBinding.Pane is now public on the binding for this
       reuse.
     - AddInHost.ShowSidebar() forwards to the controller.
     - ExplorerRibbon.OnShowSidebar wires the ribbon callback.
   imageMso = ReadingPaneShow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:10:32 +03:00
PointStar 3bd8b6dfbb ui(design): Hebrew entity-type labels + modern dialog styling
1. New WPF converters (UI.Converters/EntityTypeToHebrewConverter.cs):
   - EntityTypeToHebrewConverter  maps Case/Contact/Account/Lead/... →
     תיק / איש קשר / לקוח / ליד / ...
   - EntityTypeToBrushConverter   per-type accent brushes
   - EntityTypeToIconConverter    per-type emoji glyph

2. FileToCaseDialog redesigned:
   - Soft slate background (#F8FAFC) with card-style borders + 6px
     rounded corners
   - Search row turned into a card with a 🔍 leading icon (DockPanel.Dock
     respects RTL so it's on the visual right)
   - Group headers now show: emoji + Hebrew label (תיק / איש קשר / ...)
     + result count, with a colored stripe per entity type
   - Custom ResultItem template: hover + selected backgrounds, rounded
     selection
   - Recents pane has a header strip and per-row colored chip showing
     the Hebrew entity type
   - Primary action ("תייק") in solid blue with hover state; secondary
     ("ביטול") in white with border; tertiary ("+ צור איש קשר") as a
     muted link button

3. ComposeFromCaseDialog gets the same card + button styling for
   visual consistency.

Builds clean, 39 tests still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:06:50 +03:00
PointStar d175d4bfbb ui: move "חיפוש" to visual right + use clearer ribbon imageMso
1. FileToCaseDialog + ComposeFromCaseDialog: under FlowDirection=RTL,
   DockPanel flips Dock="Right" to render at visual left. Switch the
   search label to Dock="Left" so it renders on the right (start of
   Hebrew reading order), matching where the eye expects it.

2. Replace imageMso values that were either off-topic or rare:
   - FileToEspoCrm:       MeetingForwardToManager → MoveToFolder
   - OpenEspoCrmSettings: ServerPropertiesDialog → FileOptions
   - ComposeFromCase:     MailMergeStartLetters → NewMailMessage
   All three are present in every Outlook version ≥ 2010 and convey the
   action better.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:03:19 +03:00
PointStar 6b14e5a6a6 feat(brand): shorten ribbon button label to just "Klear"
Drop the "תייק ל-" / "File to" prefix; the action is described in the
screentip/supertip on hover. Ribbon real estate is tight and the brand
name alone reads cleaner in both locales.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:52:45 +03:00
PointStar eb081942e5 feat(brand): rename user-facing "EspoCRM" → "Klear"
User-facing surface only — display labels, button content, dialog
titles, screentips/supertips, resource strings (he + en), and the one
MessageBox referencing the brand. Internal class names (EspoCrmClient,
EspoEntityRef, EspoCrmAuthorizationException, EspoCrmHttpException,
EspoCRM-marked log messages and code comments) stay untouched — they
describe the upstream HTTP API, not the product the lawyers see.

Files updated:
- src/OutlookAddin/Ribbon/ExplorerRibbon.xml  (button label + tooltips)
- src/OutlookAddin/Ribbon/InspectorRibbon.xml (compose tooltips)
- src/OutlookAddin.UI/Dialogs/FileToCaseDialog.xaml (window title)
- src/OutlookAddin.UI/Dialogs/SettingsDialog.xaml (titles, field labels,
  About text)
- src/OutlookAddin.UI/Views/ReadingPaneView.xaml (button labels)
- src/OutlookAddin.UI/Resources/Strings.resx       (he)
- src/OutlookAddin.UI/Resources/Strings.en-US.resx (en)
- src/OutlookAddin/Services/AddInHost.cs (entity-type error MessageBox)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:52:09 +03:00
PointStar 633449548a fix(filing): accept entityName / _scope aliases + log silent target rejection
Two combined fixes for "user clicks תייק, nothing happens":

1. EspoEntityRef now also reads "entityName" and "_scope" into
   EntityType — different EspoCRM versions emit different field names
   from GlobalSearch. Without this the Submit button was clickable (Id
   was populated) but GetSelectedFilingTarget bailed out because
   EntityType was the empty string.

2. AddInHost.LaunchFileToCaseAsync now logs the dialog close outcome
   AND the rejected target shape, and pops a Hebrew MessageBox when
   filing is skipped due to missing entityType — instead of silently
   returning.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:42:48 +03:00
PointStar 41a39ce2ac fix(matching): /EmailAddress/search returns a bare array, not {total,list}
The sidebar lookup was raising
  System.Text.Json.JsonException: The JSON value could not be converted
  to MarcusLaw.OutlookAddin.Core.Models.SearchResult
because EspoCRM's /EmailAddress/search endpoint returns
[{id, entityType, emailAddress, name}, ...] (and on some hosted builds
{entityId, entityName, ...}), not the GlobalSearch-style envelope.

- IEspoCrmClient.EmailAddressSearchAsync now returns
  Task<List<EmailAddressMatch>>.
- EspoCrmClient parses the bare array, with a fallback that tolerates
  {"list":[...]} wrapping if some plugin reshapes the response.
- EmailAddressMatch accepts both the {id, entityType} and the newer
  {entityId, entityName} field naming.
- MatchingService updated for the new contract.

The GlobalSearch endpoint (FileToCaseDialog search) still uses the
{total, list} envelope and is untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:37:09 +03:00
PointStar 484e908026 fix(addin-host): treat API key alone as configured (don't require username)
After the previous fix made Username optional in the Settings dialog and
the credential store, TryInitializeCrm still bailed out when Username
was blank — leaving the add-in in "not configured" state even though
the user had saved a valid API key.

Only ApiKey is required now. The check matches the new contract used by
EspoCrmClient (X-Api-Key alone is sufficient for API-User credentials).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:31:47 +03:00
PointStar b6e4e38833 fix(creds): store empty username when not supplied (was saving "(api-key)" placeholder)
When the user left Username blank and clicked Save, the dialog persisted
the literal string "(api-key)" as the username. On reopen, that value
loaded back and the next Test Connection / save would send a poisoned
Espo-Authorization Basic header — base64("(api-key)":<apiKey>) — which
the server correctly rejects with 401, overshadowing the valid
X-Api-Key header.

Fix:
- DpapiCredentialStore.Save accepts a null/empty username and stores it
  as an empty string. ApiKey is still mandatory.
- SettingsViewModel passes null when the field is blank instead of the
  placeholder.
- EspoCrmClient already skips the Espo-Authorization header when
  username is blank — combined with this fix, no fallback header is
  sent for API-only credentials.

Tests: added Save_AcceptsEmptyUsername; updated Save_RejectsBlankInput
→ Save_RejectsBlankApiKey to match the new contract. 39/39 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:28:37 +03:00
PointStar 891b9d01ae fix(auth): send X-Api-Key header — Espo-Authorization alone was wrong for API users
EspoCRM uses TWO different auth schemes:
  * X-Api-Key: <apiKey>            — for API-type users (no password)
  * Espo-Authorization: base64(u:p) — for regular users with passwords

The PRD-derived constructor only set Espo-Authorization with a fake
base64(username:apiKey) which the server rejects with 401 for API users.

Fix:
- Always send X-Api-Key when an apiKey is provided.
- Also send Espo-Authorization Basic-style header when a username is
  supplied, so regular-user credentials still work without code changes.
- Username is now optional (constructor + Settings dialog). API key is
  sufficient on its own; Settings credential store falls back to
  "(api-key)" as the recorded username for logging when blank.

Tests updated:
- FileMailAsync_AddsBothApiKeyAndBasicAuthHeaders — asserts X-Api-Key
  is the primary header and Espo-Authorization remains the fallback.
- Constructor_RejectsBlankCredentials — empty username now allowed,
  empty apiKey / baseUrl still throw.

All 38 tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:22:23 +03:00
PointStar 661e0cb598 fix(tls): enable TLS 1.2/1.3 on AddIn startup — fix SecureChannelFailure
Symptom: "Could not create SSL/TLS secure channel" on every CRM request
(GlobalSearch, MatchingService, TestConnection). TCP and DNS succeed
fine — the failure is at the schannel handshake stage.

Root cause: .NET Framework 4.8 inherits ServicePointManager.SecurityProtocol
from the host process. In Outlook 2019/M365 that ends up as SSL3/Tls1
which modern HTTPS servers reject.

Fix: in AddInHost ctor, OR Tls12 + Tls13 into SecurityProtocol before
any HttpClient is created. Tls13 (value 12288) is wrapped in try/catch
for pre-Win10-1809 systems.

Also surface WebExceptionStatus values in the Settings "Test connection"
dialog (SecureChannelFailure / TrustFailure / NameResolutionFailure /
ConnectFailure / Timeout) so future schannel issues report a clear
Hebrew explanation instead of "One or more errors occurred".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:15:42 +03:00