act_runner v0.2.x does not expose gitea.run_attempt, so release archive and
workspace paths rendered with a trailing dash. Use gitea.run_id only, matching
the Meet/CRM deploy workflows.
Replace the once-per-session sessionStorage guard (which suppressed the loading
screen after its first appearance) with entry-aware logic: show on app entry
and reloads, suppress on internal link navigations and back/forward.
Surface wallet balance and key counts in gradient hero sections so Merchant list pages match the Events pattern.
Co-authored-by: Cursor <cursoragent@cursor.com>
Sync mobile-header-btn partials, mobile x-btn.create, and update dashboard header actions for consistent mobile FABs.
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove duplicate Billing header above Home; wallet card follows Billing
in menu order to match the desktop avatar dropdown.
Co-authored-by: Cursor <cursoragent@cursor.com>
Show wallet balance prominently in the mobile profile bottom sheet with refresh
on open; align session lifetime with the platform default.
Co-authored-by: Cursor <cursoragent@cursor.com>
Profile moves to the bottom nav sheet on small screens; optional search tab
support is synced from the Ladill platform template.
Co-authored-by: Cursor <cursoragent@cursor.com>
Storefront QR cards, slug preview, payment callbacks, and document downloads now use LadillLink so displayed and encoded links match ladl.link.
Co-authored-by: Cursor <cursoragent@cursor.com>
Public landing pages, asset paths, payment callbacks, and redirects now go through QrCode::publicPath() / LadillLink instead of ladill.com/q/* routes.
Co-authored-by: Cursor <cursoragent@cursor.com>
Storefront and landing views use publicPath() so menu, shop, and booking pages resolve asset URLs on ladl.link.
Co-authored-by: Cursor <cursoragent@cursor.com>
Drop required buyer email on public merchant forms; require phone for bookings so merchants can reach guests without Ladill accounts.
Co-authored-by: Cursor <cursoragent@cursor.com>
When a menu (food) storefront order is paid, best-effort POST it to the
merchant's Ladill POS kitchen ingest (config/kitchen.php → KITCHEN_API_URL/KEY)
so it lands on their Kitchen Display alongside dine-in tabs. Failures are logged,
never surfaced — order completion is unaffected; POS ignores orders for accounts
that don't run a POS kitchen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New Products page (merchant.products.*) with full CRUD proxied to the Ladill
CRM products API via a new CrmClient + config/crm.php (owner-scoped, type=product).
- Sidebar gains a Products entry.
- The new storefront form loads the merchant's catalog: each shop/menu section
gets an "Add from products…" picker that drops a CRM product in as an item
(name, price, description). Catalog fetch is resilient — empty if CRM is down.
Wires CRM_API_URL + CRM_API_KEY_MERCHANT on the merchant env (matches CRM).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wallet balance peek rendered as a menu row directly under Billing, backed by
a /wallet/balance endpoint (BillingClient) and guarded by Route::has. Also
opens Afia via a direct window event for reliability.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
crm.ladill.com is now live in the suite; add its launcher row + icon
(byte-identical shared launcher config).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire UserProfileMenu and shared Blade partial into topbar, mobile nav, and layouts so menu items match the account portal (Ladill Mail excluded).
Co-authored-by: Cursor <cursoragent@cursor.com>
Wallet, Billing, Team, Developers, and Support live on account.ladill.com.
Siloed sidebars keep product nav and app Settings only.
Co-authored-by: Cursor <cursoragent@cursor.com>
Guests without a local session try prompt=none SSO first; marketing pages
are only for visitors with no platform session (localhost fix retained).
Co-authored-by: Cursor <cursoragent@cursor.com>
Correct localhost marketing_url default and stop redirecting guests through
/sso/connect on visit; SSO starts only via explicit sign-in.
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove interactive OAuth fallback on silent SSO failure and add ladill.marketing_url config so app subdomains open the dashboard when signed in locally.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replies came back with **bold** markup that displayed literally to users.
Render the bubble as HTML (escaped first for safety) and convert **text** to
<strong>; newlines already handled by whitespace-pre-line.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The merchant app shipped the QR Plus Afia: the chat panel subtitle read
'Give assistant' and the system prompt identified as 'Ladill QR Plus' and
described QR-code features (Link/PDF/WiFi/styling) instead of this app's
storefronts. Rewrite the prompt for Ladill Merchant (storefronts —
shop/menu/booking, orders, payouts, fees) and fix the subtitle to
'Merchant assistant'.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AfiaController::context() called QrTypeCatalog::eventTypes(), which does
not exist on this app's catalog (leftover from the Events-app template) —
every Afia chat 500'd with 'Afia could not respond right now' before the
LLM was ever reached. Use storefrontTypes() (shop/menu/booking), the QR
types this merchant app actually owns.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Storefronts use Figtree as the default font (shop/menu, booking, book)
- Remove "Powered by Ladill" branding from the storefront header
- Fix storefront logo hiding behind the cover (logo row now relative z-10)
- Add a Share button to storefront QR downloads using the Web Share API
(native mobile share sheet for image+link; clipboard fallback)
- storefronts:import-legacy: --deactivate-source now also retires the
platform copy of already-imported (skipped) codes, no --refresh needed
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The booking editor only had bare logo/cover inputs and no Brand Color,
while the booking landing page already renders logo_path/cover_path/
brand_color and the manager already persists them. Add the same Branding
section menu/shop use so owners can set all three.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
After importing, optionally mark the platform copy inactive so Merchant
is the sole live owner and the platform stops double-counting. Public
scans still forward here (the monolith's MerchantQrForwarder routes
shop/menu by type regardless of is_active).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
One-time importer that copies legacy menu/shop storefronts (and their
products, stored inline in qr_codes.payload.content.sections) from the
shared platform DB into this app's database, so owners who created them
before the Merchant extraction can see/manage them again.
- Maps platform owners to local users by public_id (SSO sub), creating a
local user stub when missing
- Copies product images from a new read-only `platform_qr` disk
(PLATFORM_QR_ROOT) to the local `qr` disk
- Idempotent (skips existing short_codes unless --refresh); supports
--user, --dry-run, --skip-images
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Local-only sign-out: end this app's session but leave the auth.ladill.com
session intact, so the signed-out page's Sign in again re-auths silently via
prompt=none. Full sign-out of all Ladill apps remains on account.ladill.com.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SVG can embed JavaScript and would run as stored XSS when served inline on a
public storefront page, and we have no SVG sanitizer. Allow only raster
formats:
- item_images validation: image rule (permits SVG) -> mimes:jpeg,jpg,png,gif,webp
- QrCodeManagerService: reject image/svg+xml / .svg(z) in brand-image and
item-image storage (defense in depth, since those only checked the image/* prefix)
- file pickers: accept raster mimes only
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The storefront create/edit/manage UI and the public storefront page were
give's church-donation flow relabeled. Replace with genuine merchant
storefronts, reusing the QR-core manager + validator (which already build
sections/services content):
- Merchant create: type picker (shop/menu/booking) + per-type editors in a
shared partial (products/menu items with prices; bookable services with
days/hours). x-if per type so inputs never collide across types.
- StorefrontController store/update/create now delegate to QrCodeManagerService
for all three types (was hardcoded church org_type/denomination/collection).
- Storefront show = QR preview + download + live toggle + delete + full editor.
- QrCodeManagerService: shop/menu/booking are free (no QR-wallet gate); drop
the duplicate church TYPE_SHOP arm in hasContentChanges.
- Public: new storefront catalog+cart view for shop/menu (posts items[] to the
existing order/Pay flow); route shop+menu to it. Booking already had a real
public page; church TYPE_SHOP landing branch retired.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The give→merchant transform left several independent breakages that 500'd
once a logged-in user hit the pages:
- /storefronts/{create,show,update,...} were wired to Qr\QrCodeController,
which renders events-lineage qr-codes.* views referencing 14 undefined
events.* routes. Rewire to StorefrontController (clean storefronts.* views).
- storefronts/show.blade.php @include('give.storefronts.partials.*') — the
view dir was renamed give→merchant but the include namespace wasn't.
- /payouts + /search queried qr_sale_orders.*_minor; migration created *_ghs.
- Remove dead GiveDonation model (nonexistent give_donations table).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The customize script's blind TYPE_CHURCH→TYPE_SHOP and churchLogo→menuLogo
replacements created a duplicate QrCode::TYPE_SHOP constant and duplicate
QrScanController::menuLogo()/menuCover() methods — fatal at request time
(crashed /dashboard with 500). The menu* methods already cover TYPE_SHOP,
so drop the redundant church-origin copies.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>