Skip to content

Feat/catalog no code ux - #501

Closed
Dev-Ruco wants to merge 255 commits into
ArnasDon:mainfrom
Dev-Ruco:feat/catalog-no-code-ux
Closed

Dev-Ruco wants to merge 255 commits into
ArnasDon:mainfrom
Dev-Ruco:feat/catalog-no-code-ux

Conversation

@Dev-Ruco

Copy link
Copy Markdown

Summary

What changed

Test plan

  • npm run typecheck clean.
  • npm run lint — no new errors beyond the pre-existing backlog.
  • npm run build succeeds.
  • Feature / fix manually exercised in the browser (or the reason it can't be).

Related

Dev-Ruco added 30 commits August 5, 2026 21:15
fix(ai): resume auto-reply through service-role client
Integrar conhecimento verificado da NOVYRA
feat(whatsapp): enforce 24-hour customer service window
Adds a manually selectable Portuguese catalogue, persists the chosen locale in a first-party cookie, and localises the existing WhatsApp 24-hour service-window interface.
Dev-Ruco and others added 27 commits August 11, 2026 11:21
New 'Avaliação' tab in /agentes (admin+): runs the golden-set cases and
customer-simulation personas one at a time, using the account's own
already-configured AI key — no shell/env vars needed. Each run shows
its score, criteria breakdown, and exactly which tools were called, so
tool-decision regressions are visible without leaving the browser.

- GET /api/ai/eval/cases — metadata only, no LLM calls.
- POST /api/ai/eval/run-one — runs exactly one case/persona by id,
  resolved server-side against the fixed known sets (never accepts
  case content from the client, so it cannot be redirected at
  arbitrary text on the account's own key).
- New aiEvalAccount rate limit (3/hour) — each run is dozens of real
  model calls against the account's own spend.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- learnings.md + a short protocol appended to AGENTS.md: read at the
  start of a session, append real lessons at the end, escalate to a
  durable rule if the same mistake repeats. Seeded with 5 real lessons
  from this session (git-sync between two Claude Code sessions,
  external-catalogue schema drift, colour gender agreement, the two
  separate tool_key CHECK constraints, migration timestamp collisions,
  and the GRANT-vs-RLS bug).
- New src/lib/ai/pricing.ts: real published USD/1M-token pricing for
  gpt-5.4-mini and claude-haiku-4-5 (checked August 2026), returns null
  for any unpriced/custom model rather than guessing.
- New ai_configs.usd_to_mzn_rate (editable in Configuração, seeded at
  63.91, the real rate at time of writing) — /api/ai/usage now computes
  real cost per model and shows it in Metical in the Utilização tab,
  alongside the USD figure and a note when a model has no known price.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The exchange rate used to convert token spend into Metical (shipped in
d05320d) was a manually-typed settings field, which drifts stale the
moment the real rate moves. Replace it with an automatic fetch from
BCI's public daily exchange-rate page (bci.co.mz/cambio), cached on
ai_configs for 6 hours to avoid hitting their site on every dashboard
load, with a three-level fallback (fresh cache -> live BCI fetch ->
stale cache -> hardcoded default) so a network hiccup or markup change
never breaks the usage page.

Settings now shows the rate read-only, with a "last updated" timestamp,
instead of an editable input -- editing it locally would have silently
fought the next automatic refresh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The GET handler returned a generic "Failed to load AI configuration"
on any select error but never logged what actually went wrong,
making a live report of that message undiagnosable from server logs
alone.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Audited the whole customer-facing pipeline (reply cap/handoff, tool
calling, guardrails, prompt) before opening the agent to real
customers. The core pipeline (buffering, atomic reply-slot claim,
webhook idempotency, handoff notices) checked out safe. These fixes
close the real gaps that surfaced:

- dispatchPendingActions no longer aborts the whole photo batch when
  one WhatsApp media send fails -- each send is isolated, and the
  customer gets photos 1 and 3 even if photo 2 failed, instead of a
  generic apology and silently dropped remaining photos. A new
  MEDIA_SEND_FAILURE_NOTICE only fires when every send in the batch
  failed; a partial failure is just logged.
- New stock- and size-honesty system prompt rules: the agent now
  checks a product's real stock value before implying it's available,
  and admits honestly when size data isn't in the catalogue instead of
  guessing.
- New prompt clause for a brusque/impatient (but not complaining)
  customer: stay calm, don't mirror tone, and don't hand off on tone
  alone.
- buildSearchVariants gains an opt-in size-cue detector ("tamanho M",
  "numero 38") so a customer asking by size doesn't silently get zero
  matches -- gated behind an explicit size-indicating word so it can't
  flood every search the way a naive short-token match would.
- Trimmed noisy DOMException dump in the exchange-rate BCI-fetch log.
- Three new catalog eval golden-set cases (out-of-stock, size
  fallback, rude-tone) exercising the new rules against the existing
  fixture-tools harness.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…t gap

Investigated the account owner's concern that no user can configure the
agent's tools for their own business -- everything was fixed in code,
even though every account on this platform is a different business.
Two concrete bugs surfaced alongside that architectural gap:

- The live auto-reply path always calls buildSystemPrompt with an empty
  knowledge array (by design -- knowledge only reaches it via the
  search_knowledge tool call), so the "trust these excerpts, don't
  guess" framing that exists for the draft path never reached real
  customer conversations. search_knowledge's description and JSON
  result now carry that same framing directly, since that's the
  channel the live path actually uses.
- get_style_opinion (a fashion-styling tool) was enabled by default for
  every new account regardless of business type, unlike every other
  business-specific tool (add_tag, create_deal, schedule_visit), which
  require explicit opt-in. Flipped to match that pattern.

Also added two prompt rules for cases the agent had no guidance on:
a message too vague for any tool to resolve (ask a natural clarifying
question instead of a canned line), and a capability that's simply not
enabled for this account (never claim a tool it doesn't have, or a
tool by name).

For the architectural gap itself: agent_tools gains a free-text
`instructions` column, editable per tool in Agentes -> Ferramentas,
appended to that tool's built-in description at request time. This is
the cheapest lever toward "every account configures its own tools" --
letting any business tailor a fixed tool's behavior (e.g. "we don't
schedule visits on Sundays") without a code change, without building a
full custom-tool system.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found in a real production conversation: the customer asked for a
styling opinion and the reply included this, sent verbatim as if it
were the message itself:

  "Veja estas opções:\n\n[Opção interactiva no WhatsApp]\nSeleccione
  este produto abaixo...\n\n[Opção interactiva no WhatsApp]..."

buildConversationContext annotates past media/interactive messages
with bracketed notes like this so the model can read its own history
-- they were never meant to be reproduced. The model picked up the
pattern from its own prior turns and echoed it back as literal reply
text, which is about as clear a "this is obviously a broken robot"
moment as a customer can get.

Two layers: a new prompt rule explicitly tells the model these
brackets are for its own understanding only, and guardrails.ts now
blocks (and hands off) any reply that contains one of these markers,
the same mechanism already used for leaked [[CONTROL]] tokens.

The marker list moved into its own history-annotations.ts module
rather than being exported from context.ts directly -- auto-reply.ts's
tests mock the whole context module, which would have silently broken
guardrails.ts's import of it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reviewed real conversation history and found three concrete
conversational gaps in the same "Catalogue selling rule":

- "Nao tem mais tipos?" got the exact same 3 products resent,
  verbatim, twice in one conversation. The old wording actually
  licensed this: "repeat all prior options unless the customer asks
  for more choices" reads backwards -- asking for more should mean
  showing something different, not resending the same list.
- "Tem mais cores disponiveis?" (about a specific conjunto already
  shown) got three completely unrelated products back instead of
  colour variants of the item actually being discussed.
- A customer asking "quanto custa" / "so quero o preco" three times
  in a row got the same qualifying question (compression preference)
  repeated three times, never a price, until a human intervened.

Rewrote the rule to require genuinely different results when asked
for "more", resolve colour/variant follow-ups against the specific
item already in context, and drop a qualifying question once the
customer has answered it or pushed back, answering with the best
available info (e.g. a price range) instead of asking again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…d with the tool loop

Followed up on a full architecture audit (agent runtime vs. a
sophisticated prompt layer) with the concrete gaps it surfaced: no way
to scope agent behaviour per business objective without growing the
one shared prompt scaffold, no sampling-temperature control, and three
of the AI surfaces (draft, Playground, operator-reply) never used the
real tool-calling loop the live auto-reply bot runs, so what an agent
tested in the Playground did not reflect what a customer actually got.

- New wacrm.skills table: a named bundle of an objective prompt
  fragment plus a subset of the existing 8 tools, scoped per agent.
  Purely additive: an agent with zero skills configured behaves
  exactly as before. Wired into auto-reply.ts prompt composition and
  tool permission narrowing (agent_tools stays the ceiling;
  handoff_human is exempt from narrowing, since it is the safety
  valve, not a business capability). Minimal CRUD UI under Agentes ->
  Skills.
- ai_configs gains an optional temperature column (null means provider
  default, unchanged behaviour for every existing account), threaded
  through generate.ts into both provider adapters, with a UI toggle.
- draft and Playground now run the same tool-calling loop as
  auto-reply, scoped to PREVIEW_SAFE_TOOL_KEYS (catalogue/knowledge/
  style-opinion search), never a mutating tool (create_deal, add_tag,
  schedule_visit), since a draft or Playground turn can be discarded
  without ever being sent. Playground has no real conversation or
  contact, so its tool calls skip the per-call agent_tool_calls
  telemetry insert (see the conversationId guard in tools/index.ts).

Migrations are included but not applied to any database. Run them via
your normal Supabase migration flow before deploying.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The usage dashboard showed "preco desconhecido" for gpt-5.4-nano since
it was never added to MODEL_PRICING. Sourced current OpenAI pricing
(0.20 dollars per million input tokens, 1.25 per million output) from
OpenRouter and Requesty.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ity, memory, overview)

Follow-up to the architecture audit and its critical evaluation: the
single ai-config.tsx form conflated identity, behaviour, credentials,
commercial strategy and handoff destination into one screen, with no
visibility into what the agent actually had available. Split it into
one shared config hook plus per-concern tabs, matching the reference
architecture from the evaluation: Overview / Identity & Behaviour /
Skills / Tools / Knowledge / Memory / Security & Handoff / Model &
Runtime / Playground.

Deliberately excluded, per the same evaluation: behaviour sliders with
real runtime effect (would require decomposing the fixed prompt
scaffold in defaults.ts -- a Context Builder change, not a UI change)
and any skill-selection/routing mechanism (skills already fold into
the prompt as structured reading material for the model's own
judgement, not as router input).

- New agent identity fields (name, role, language, description) on
  ai_configs -- optional, only change buildSystemPrompt's opening line
  when set.
- Skills gain objective / when_to_use / when_not_to_use, rendered as
  labelled sections in skillsPrompt() so the model has clearer material
  to reason over -- no selection logic added, every enabled skill is
  still always active together, exactly as before.
- Knowledge documents gain a free-text type label and a status
  ('active'/'archived'). Archiving is UI-facing metadata only in this
  pass -- retrieveKnowledge/the match_ai_knowledge_* RPCs are
  unchanged, so an archived doc still surfaces in retrieval; excluding
  it would mean editing the retrieval runtime, out of scope here.
- New Segurança & Handoff tab: the two existing config fields (reply
  cap, handoff destination) plus a read-only mirror of the real
  handoff/guardrail rules in route.ts/guardrails.ts. Not editable, by
  design -- a business admin should never be able to switch off a
  safety rule through this screen.
- New Memória tab: view + delete over contact_memories. No
  categorization, no change to the extraction logic in
  contact-memory.ts.
- Playground now returns an ephemeral per-turn execution trace (active
  skills, tools called, knowledge sources used, guardrail result) --
  never persisted, since there is no real conversation for
  agent_traces to attach to. Tools tab shows last-used timestamp and
  which skills reference each tool.
- Retired the monolithic ai-config.tsx (738 lines, only ever used from
  this one page) in favour of use-agent-config.ts (shared fetch/save
  state) plus agent-identity.tsx / agent-runtime.tsx /
  agent-security.tsx / agent-overview.tsx.

Three additive, nullable-column migrations; none applied to any
database by this change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…d in interface audit

Ran a full better-interface (accessibility/layout/writing/typography/
colors/ui) audit of the Agentes page against the real code (no live
render -- auth is against a production-connected Supabase instance,
so signing in to screenshot was avoided). Presented two redesign
options; this implements the chosen one plus every finding the audit
surfaced.

- New AgentBuilderShell: replaces the flat 13-item horizontal tab bar
  with a grouped sidebar (Visao Geral / Configurar / Controlar /
  Testar / Observar), matching the grouping the flat tab list had no
  way to express. Modeled as navigation (buttons + aria-current, not
  an ARIA tablist) and copies the primary app sidebar's own mobile
  drawer mechanics (translate-x, backdrop, Escape, scroll lock) rather
  than inventing a new pattern. Every section stays mounted (hidden
  via the `hidden` attribute) when inactive, so switching away from
  Playground no longer drops the test conversation.
- Fixed the two HIGH accessibility findings: icon-only delete buttons
  (skills, memories) had no accessible name; the API-key show/hide
  toggle had tabIndex={-1}, making it a pointer-only control (a
  pre-existing bug inherited from the old ai-config.tsx when it was
  split up).
- CardTitle gains an opt-in `as` prop (defaults to "div", unchanged
  everywhere else) so a title can render as a real h3. Applied across
  every CardTitle in the Agentes tree -- the page had almost no
  heading outline for assistive tech, one root cause (the shared
  div-only component), thirteen call sites.
- Playground's execution-trace disclosure now reports aria-expanded.
- Visao Geral: replaced seven border-b divider rows with two spaced
  groups; unified max-w-2xl/max-w-3xl inconsistency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Improves catalogue retrieval, product presentation, conversation catalogue state, media handling and AI category reclassification.
CATEGORY_ALIASES/COLOR_ALIASES lived as two separately hardcoded (and
already drifted) constants in agent-search.ts and search.ts, baking
one tenant fashion vocabulary (LC Fitness) into the shared retrieval
and ranking engine. Any other vertical (car rental, clinic, etc) got
no category/colour aliasing at all, and could not get any without a
code change.

Adds wacrm.catalog_taxonomy_terms (account_id, kind, canonical_value,
aliases) and src/lib/catalog/taxonomy.ts, which loads an account own
taxonomy and falls back to a single consolidated default list (the
old two divergent copies, merged) only for accounts with nothing
configured yet. agent-search.ts and search.ts now take the groups as
a parameter instead of importing hardcoded constants; every existing
call site keeps its old default behaviour via default parameters.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
src/app/api/meta/catalog-feed/route.ts resolved its one Meta Commerce
Manager feed by ilike-matching a catalog_sources row named literally
'Base LC Fitness', with no account_id filter at all and no
authentication on the public GET endpoint. It could not serve a
second tenant without a code change, and if any other account ever
named a source the same thing, this endpoint could serve the wrong
tenant catalogue to the public internet.

Adds catalog_sources.meta_feed_token (256-bit, built from two
gen_random_uuid() calls so no extension dependency), and a new
/api/meta/catalog-feed/[token] route that resolves a source only by
its exact opaque token -- never by name, never by an account_id
supplied by the caller. The CSV builder (src/lib/catalog/meta-feed.ts,
extracted from the old route) uses the source own configured name for
the brand/filename instead of a literal 'LC Fitness' string.

The old fixed URL now only 308-redirects to a token configured via
LEGACY_META_FEED_TOKEN (set by operators during migration), and
otherwise 404s -- it has zero knowledge of any tenant.

Also wires the settings UI (database-integrations.tsx) to show each
connected source own feed URL with a copy button, and to select
meta_feed_token in the sources API so that URL is available to show.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
buildSystemPrompt() in defaults.ts is the shared scaffold every
tenant on the platform gets, regardless of vertical. It unconditionally
included a 'Personal styling rule' (body, fitness habits, style
preference wording, referencing get_style_opinion and 'the brand') --
fashion-retail language a clinic or car-rental tenant would also
receive even though it never applies to them. The 'Image handling
rule' kept its useful generic mechanism (photo -> describe -> search
catalogue) but dropped the word 'garment' for 'product'.

The styling rule itself was not deleted -- it moved verbatim into a
new LC Fitness Skill ('Consultoria de Estilo'), seeded by migration
together with LC's own category/colour taxonomy data (including
'pantalona', previously unsupported by the hardcoded core list --
proof the mechanism works through configuration, not a code change).
Skills already only apply to the account/agent they are configured
for, so this is a behaviour-preserving move for LC, not a new
capability.

Also appends a learnings.md entry on the actual root cause of the
'category: null' reports for LC products (a missing settings-UI field
for catalogCategory, not the taxonomy/matching layer -- fixed in the
previous commit alongside the feed-token settings screen).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The 'Reclassificar tudo com IA' vision prompt in classify/route.ts had
a fixed instruction listing fashion categories (legging, top, camisola,
t-shirt, ...) in Portuguese for every account, regardless of vertical.

buildClassificationSystemPrompt(knownCategories) now builds that
instruction from the calling account own wacrm.catalog_taxonomy_terms
rows (loadAccountCategoryTerms -- deliberately no fallback to the
generic defaults used by search, since leaking LC's fashion vocabulary
into another tenant's classification prompt would defeat the point).
An account with nothing configured still gets a fully working,
domain-neutral classifier that proposes its own category from the
image instead of matching a suggested list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The setting was already fully per-account configurable end-to-end
(ai_configs.auto_reply_max_per_conversation, /api/ai/config, the
Agentes -> Seguranca UI) -- the only real blocker for an account like
LC Fitness that legitimately needs a longer bounded auto-reply run
was the hard CHECK constraint capping it at 20. Raises the DB
constraint, the API clamp and the UI max in lockstep; the protection
itself is unchanged, only its ceiling. LC can now be configured up to
30 (or any bounded value) directly in Agentes -> Seguranca, no code
change needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
loadCatalogTaxonomy() still fell back to DEFAULT_CATEGORY_GROUPS/
DEFAULT_COLOR_GROUPS (legging, camisola, top, macacao, sapatilha, ...)
whenever an account had no configured taxonomy or the lookup failed.
That was still business-specific hardcode sitting in generic runtime
code, even though it was consolidated from two copies into one. LC
already gets this exact vocabulary through its own tenant migration
(20260813093000_lc_fitness_taxonomy_and_style_skill.sql), which is the
correct place for it.

An account with no configured taxonomy (or a failed lookup) now gets
empty category/colour groups, not a hardcoded list. This does not turn
off matching: buildSearchVariants and categoryMatches/colorMatches
already fall back to plain substring/token matching on the raw
requested text when no alias group applies -- that generic textual
matching keeps working with zero configured taxonomy, just without
synonym/gender-inflection expansion.

search.ts no longer imports any default vocabulary either -- its
categoryGroups/colorGroups parameters now default to empty arrays.

Tests updated: taxonomy.test.ts asserts LC's own account gets LC's
aliases, a car-rental account gets its own (isolated) aliases, an
unconfigured account gets empty groups (never LC's), and a DB error
also yields empty groups, never LC's. search.test.ts now proves the
same generic mechanism by passing explicit sample groups instead of
relying on an implicit default, plus new cases proving no expansion
happens with none supplied. agent-search.ts needed no code change --
its matchers already treat taxonomy as a plain parameter.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tools

buildSystemPrompt()'s universal scaffold unconditionally included the
Catalogue selling rule, Stock honesty rule, Size honesty rule and Image
handling rule -- product/stock/size/photo instructions every account on
the platform received even a support-only or booking-only tenant with
no search_catalog/send_product enabled at all.

Extracts them into catalogueCapabilityPrompt(), injected by
buildSystemPrompt only when a new hasCatalogueCapability flag is true.
No new framework -- one function, one boolean, computed from the
account's own already-loaded tool permissions
(search_catalog || send_product) at each of the 6 call sites (auto-
reply, draft, playground, operator-reply, and the 2 eval helpers).

draft/playground/operator-reply had to move their existing permission-
loading block to run before buildSystemPrompt instead of after, since
the flag has to exist before the prompt is built; no new DB calls were
added, the same loadAgentToolPermissions call just runs earlier.
operator-reply computes the flag from the account's raw permissions
(matching how the other three routes do it), not from its own further-
narrowed read-only tool subset, so LC's prompt is unchanged from
before this commit.

LC has both tools enabled account-wide, so this is behaviour-preserving
for LC on every call site; a support-only account now correctly never
sees product/stock/size/photo instructions it has no tool to act on.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
An admin can now create/edit/disable/delete their own category and
colour vocabulary (wacrm.catalog_taxonomy_terms, already in place from
the previous SaaS-hardening branch) entirely from the UI -- no SQL, no
migration, no code change, no redeploy. New endpoints:
GET/POST /api/catalog/taxonomy, PATCH/DELETE /api/catalog/taxonomy/
[id], all scoped by the caller own account_id. GET also returns an
approximate product count per term so deleting one is an informed
decision.

Adds two small reusable UI primitives this needed anyway:
CollapsibleEditor (view-mode summary, click to reveal an edit form,
Guardar/Cancelar) and AttributeSelect (a searchable category/colour
picker with inline "+ Criar" instead of a free-text input, so
'pantalona'/'Pantalona'/'PANTALONA' cannot become three different
categories).

classify/route.ts now snaps the vision model output to the account own
canonical value when it matches one of the account configured aliases
(the AI can still propose a brand-new category when nothing matches).

Adds jsdom + @testing-library/react + @testing-library/user-event as
dev dependencies -- the repo had no way to test interactive component
behaviour (search/select/create-new, expand/collapse) before this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The bulk uploader used a bare <input type="file" multiple> ('Choose
Files / No file chosen'). BulkUploadDropzone replaces it with a real
drop zone: drag & drop, click-to-pick via an accessible <label>
wrapping a visually-hidden input (native keyboard/screen-reader
behaviour, no hand-rolled focus handling), non-image drops filtered
out client-side.

BulkItemCard shows each photo per-item state individually --
uploading, 'A analisar com IA...' while classifying, a 'Pronto' or
'Falta informação' badge, and its own error text -- instead of one
undifferentiated grid, and uses AttributeSelect for category/colour
instead of free text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ive AI reclassify

Catálogo now defaults to Produtos (internal catalogue). External
integrations (REST API + external Supabase, previously two of three
equal tabs) move into one 'Integrações externas' tab, clearly labelled
Beta with the same amber-pill pattern already used for Flows, and
introduced by copy explaining it is optional -- a new account is never
steered toward configuring an external database just to use the
agent. Nothing about those integrations was removed or altered.

ProductCard: view mode shows photo/name/price/category·colour/state
only, a 'Pronto'/'Falta informação' badge, and category/colour editing
now goes through AttributeSelect instead of free text; unchanged
otherwise (Editar expands, Guardar/Cancelar collapses back).

'Reclassificar tudo com IA' becomes 'Organizar produtos com IA' behind
a real confirmation dialog (not a browser confirm()): shows how many
products will be reviewed, and a choice between 'Preencher apenas
campos vazios' (default -- never touches a category/colour/description
a person already filled in) and 'Rever todos' (explicit warning that
existing data may change). Progress and a final summary (classified /
needs review / failed, failures listed by name, never hidden) render
in the same dialog.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every Skill (and every Tool) rendered as a permanently-open card --
name, objective, when-to-use/not, instructions, tool checkboxes all
visible at once for every row, regardless of whether it was being
edited. With more than a couple of skills configured this was a very
long page.

Both now use the shared CollapsibleEditor: collapsed shows a summary
(name, ACTIVA/INACTIVA, objective, tool count for Skills; icon, title,
last-used/used-by for Tools), click to expand into the actual edit
form, Guardar/Cancelar collapses back with a toast and an updated
summary -- one row open at a time is not enforced, but nothing is open
until the admin asks for it.

Skills' enable Switch and Tools' enable Switch both moved out of the
clickable header into CollapsibleEditor's new headerActions slot (a
sibling of the toggle button, not nested inside it) so flipping a
switch never also opens the edit form -- nesting an interactive
control inside the header button would have been invalid markup
either way.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ager

'Pantalona' / 'pantalonas, calça pantalona, wide leg' / 'Preto' /
'preta, black' were LC's own example values shown as input placeholders
to every account, including a car-rental or clinic tenant that will
never see anything resembling their own business in the form they use
to create their first category. Not a functional leak (nothing reads
these strings), just an example that should not carry one tenant's
vocabulary into a generic UI. Replaced with sector-neutral placeholder
text ('Ex.: Categoria principal' / 'sinónimo 1, sinónimo 2' / 'Ex.: Cor
principal').

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Dev-Ruco
Dev-Ruco requested a review from ArnasDon as a code owner August 13, 2026 12:50
@ArnasDon

Copy link
Copy Markdown
Owner

Thanks for sharing this. Per CONTRIBUTING.md this repository is a template: forks are expected to diverge, and upstream only takes small, scoped fixes (security, correctness, docs) opened against current main. This PR is a whole-fork snapshot (rebrand/stack/deploy changes, colliding migration numbers, and it now conflicts with main), so I am closing it rather than reviewing file by file. If there is a specific bug fix in here that applies to the upstream template, please open it as its own small PR — those land quickly.

Same note as on #475/#476: the private wacrm Postgres schema and the NOVYRA catalog integration are fork-specific.

@ArnasDon ArnasDon closed this Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants