Commit Graph

24 Commits

Author SHA1 Message Date
ec5e4f6c78 admin: Slice 3g catalog attributes UI
- hooks: flat categories, attributes + attribute-values list/CRUD; row types.
- /admin/catalog: attributes master (create/edit modal with category
  multi-select, delete) → per-attribute values panel (inline add/edit/delete).
- sidebar "ویژگی‌ها" nav link.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 16:01:01 +03:30
ef7804df0e admin: Slice 3f homepage/merchandising UI
- hooks: banners/categories/seller-tiles list + shared update/delete
  mutations; row types.
- /admin/homepage: tabbed بنرها/دسته‌ها/فروشگاه‌ها with image thumbnails,
  active toggle, seller-tile reorder, delete.
- sidebar "صفحه اصلی" nav link.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 15:55:22 +03:30
b4faa9d180 admin: Slice 3e notifications UI
- hooks: notification types (CRUD), sent list, overview, sms blacklist
  (list + delete); NotificationType/Notification/SmsBlacklist row types.
- /admin/notifications: tabbed — قالب‌ها (template CRUD modal), ارسال‌ها
  (overview KPIs + sent list), لیست سیاه (unblock).
- sidebar "اعلان‌ها" nav link.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 15:11:36 +03:30
97948767a0 admin: Slice 3d moderation UI (comments + chat)
- hooks: useAdminComments/ChatMessages, useAdminCommentModerate/Delete,
  AdminCommentRow/AdminChatMessageRow types.
- /admin/moderation: tabbed page — comments (hide/unhide/delete, filter by
  visibility & top/reply) and read-only chat browse.
- sidebar "مدیریت محتوا" nav link.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 15:05:07 +03:30
b3189db773 admin: Slice 3c AI-health dashboard UI
- useAiHealth hook + AiHealthOverview type (60s refetch).
- /admin/ai-health: per-pipeline sections (try-on, metadata, instagram,
  apgs) with status pills, throughput KPIs, and recent-failure lists.
- sidebar "سلامت AI" nav link.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 14:57:42 +03:30
c1118f70d6 admin: Slice 3b discounts & collections UI
- hooks: useAdminDiscounts/Create/Update/Delete, useAdminCollections/Update/
  Delete, AdminDiscountRow/AdminCollectionRow types, adminDelete helper.
- /admin/discounts: table + create/edit modal form + delete + validity badge.
- /admin/collections: table + is_public toggle + delete (moderation).
- sidebar "تخفیف‌ها" + "مجموعه‌ها" nav links.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 14:48:50 +03:30
7ad3fccd6e admin: Slice 3a audit-log viewer
- useAuditLog hook + AuditLogRow type.
- new /admin/audit route: action/target filters, search, and a compact
  before→after diff of the touched fields per entry.
- sidebar "گزارش فعالیت" nav link.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 13:33:59 +03:30
40052b7002 admin ledger: Slice 2c journal UI (derived balances + journal history)
- use-admin-hooks: LedgerOverview.journal, LedgerEntryRow,
  LedgerSellerDetail journal fields, useLedgerJournal.
- new "دفتر روزنامه" sub-tab + admin.ledger.journal route.
- ledger overview: journal-derived KPI row + recorded-vs-journal drift badge.
- seller detail: journal-balance stat + journal history table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 13:24:06 +03:30
24d4c8a302 feat(admin): payout approve/reject actions on the ledger (slice 2b)
Approve (complete) / reject buttons on pending payouts, hitting the new admin
action endpoint (which debits the balance + journals on approve). Toasts + list
and overview invalidation on success.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:44:59 +03:30
cedeef4746 feat(admin): ledger UI — overview/reconciliation, payouts, transactions, per-seller (slice 2a)
Ledger section with sub-tabs: overview (money held/owed, payouts by status, and
a reconciliation warning card), payouts + transactions lists, per-seller balances
list, and a per-seller detail showing recorded vs derived balance + discrepancy,
bank accounts and payout history. Enabled the Ledger nav item.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:28:11 +03:30
935c337df0 feat(admin): monitoring boards UI — orders/sellers/users/products (slice 1)
- Reusable admin primitives: AdminTable, AdminPagination, AdminToolbar,
  AdminSearch/Select, StatusBadge + fa number/date/toman + useDebouncedValue.
- Four board routes with search, filters, paging:
  orders (read-only), sellers (approve/reject/verify), users (ban/unban),
  products (active toggle). Wired into the admin sidebar nav + hooks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:17:22 +03:30
0360780411 feat(admin): admin-panel foundation shell + overview (slice 0)
- /admin opted out of shopper chrome in MyLayout; /admin added to server
  protectedRoutes; useRequireAdmin() gates to superusers (UserProfile.isSuperuser).
- Admin shell (admin.tsx) with sidebar nav (Overview live; other sections stubbed)
  and a mobile top bar; Overview dashboard (KPI cards + 14-day orders chart) via
  use-admin-hooks.ts hitting /api/adminpanel/v1/overview/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:00:49 +03:30
6a300b8760 fix(store): don't bounce owners home, stop cross-user myStores cache
- app/hooks/useStoreOwnership.ts: the old redirect logic sent owners to /
  the moment useMyStores briefly resolved to []. That empty array came from
  useMyStores's dead `if (!token) return []` inside the queryFn, so any
  transient auth blip left the store page thinking the user has no stores.
  Now compares usernames case-insensitively, only redirects while the query
  is settled (!isLoading && !isFetching && !isError), and never bounces to
  home on empty — worst case the seller sees an empty dashboard rather than
  a silent kick-out.
- app/requestHandler/use-seller-hooks.ts: removed the unreachable
  `if (!token) return []` in useMyStores and added the token to the
  queryKey so a login switch stops serving the previous user's cached list.
- app/routes/store.$storeId._index.tsx: mount StorePushPromptCard at the
  top of the store dashboard.
- app/routes/store.$storeId.tsx: sidebar "پیام‌ها" badge reads
  badges.sellerChat[storeId] (per-store) instead of the buyer-side
  badges.chat, matching the badge-serializer split.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-18 13:18:23 +00:00
7867db425e feat(chat): share the product into the chat + fix empty-thread preview
- Product-page chat buttons pass the product id when starting a thread, so it's
  shared into the conversation as a card the seller can see (and tap through).
- MessageBubble renders the product card (image, title, price → product page);
  card-only messages no longer render an empty bubble.
- Inbox preview shows 🛍 <product> instead of "بدون پیام" for product-card
  threads. ChatProductCard type + product on message/last-message types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:09:01 +03:30
5c8e19001c feat(seller): inline quick-edit for product & variant prices
Sellers can tap a price on the products page to edit it inline (Enter to
save / Escape to cancel), mirroring the existing stock quick-edit.

- new reusable InlinePriceEditor (optimistic display, Persian/Latin digit
  input, thousands grouping, loading + toast feedback, a11y labels)
- base price editable on each product row (mobile + desktop column)
- per-variant price editable beside stock in the expanded panel; empty
  clears the override and inherits the base price ("پیش‌فرض: <base>")
- useUpdateProductBasePrice + useUpdateVariantPrice hooks; extend generated
  API client + types for the two new endpoints

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 12:41:04 +03:30
0d0da636fd fix(seller-team): call /api/seller/v1/ (not /api/seller/) for team endpoints
my-stores, members list/invite/remove all mount under /api/seller/v1/ in the
Django backend, so my-stores and every team-management fetch was 404'ing. That
made useMyStores throw silently, useStoreOwnership fall back to role=null, and
the setting page's `!ownerOnly || role === "owner"` filter drop every
owner-only tile (edit-store, team, financial-dashboard, shipping-method,
instagram-sync). Owners could log into their own store and see none of the
owner-gated menu items.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-06 12:48:36 +00:00
bb44ebf0e5 fix(seller-team): guard owner-only routes + robust useMyStores
Review fixes:
- useMyStores throws on a failed response instead of resolving to [] — a
  transient 401/blip no longer looks like "no stores" and bounces an owner off
  their own dashboard (query stays in error/retry, keeps prior data).
- Add useRequireOwner() guard and apply it to the owner-only routes
  (financial-dashboard + its cash-funds/reports/transactions sub-routes and
  shipping-method) so staff who hit those URLs directly are redirected to the
  dashboard. (Financial data was already backend-protected; this is the matching
  client-side gate.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 17:04:06 +03:30
a642fc0fae feat(seller-team): multi-user dashboard — store switcher, role gating, team UI
- useMyStores + team hooks (invite by phone / list / remove) in use-seller-hooks.
- useStoreOwnership rewritten to allow staff (not just the owner) via my-stores;
  exposes `role` so callers can gate owner-only UI, and no longer redirects
  members away from a store they help manage.
- Store settings: owner-only items (financial, shipping, edit store, instagram
  sync, team) hidden from staff; new "مدیریت اعضای تیم" entry (owner-only).
- New /store/:storeId/team page: invite by phone + members list with pending/
  active status + remove (owner-only, redirects staff). MyLayout keeps store mode
  on the team route.
- StoreForm edit is owner-only (redirects staff). Profile "enter my store" now
  routes owners to their store and staff to the store they manage.
- Chat already split earlier: seller inbox vs personal buyer inbox.

Depends on the seller-team backend; ships on this branch, not main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 16:43:53 +03:30
3502168a8b feat(seller-team): split buyer inbox vs store inbox in chat UI
Seller dashboard threads (type="store") now fetch the store's inbox via
useGetStoreThreads(storeId) -> /threads/list/?store_id=; the profile threads page
(type="user") keeps the personal buyer inbox. Depends on the seller-team backend
(store-aware threads + store_id-by-username), so it ships on this branch, not main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 16:00:23 +03:30
660e01c3c3 feat(seller): customizable profile banner + seller page polish
- Banner editor in store edit: color swatches + custom color picker, or
  image/GIF upload with a 1600x500 dimension hint. Renders the chosen banner
  (image cover / solid color) on the public seller page, falling back to the
  default gradient.
- Sellers list: drop the desktop name overlay (match mobile, image-only tiles).
- Seller page mobile header: chat moved top-left under the banner, search+filter
  beside the follow button, followers/product counts removed (rating kept),
  compacted so products fill more of the screen, circular logo, thinner action row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 12:00:23 +03:30
bb10907463 feat(product): record product views for recommendations
Fire a fire-and-forget view event when an authenticated, non-owner user
opens a product page (deduped per product), POSTing to the new
/api/engagement/v1/views/ endpoint. Silent on failure so tracking never
disrupts browsing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 13:57:04 +03:30
940cc3e859 fix(store): always revalidate public storefront product list
The seller store page (useGetSellerProducts) inherited the global 30s
staleTime with refetchOnWindowFocus disabled, so after a seller edited a
price the store tiles could keep showing the pre-edit price while the
(SSR) single-product page already showed the new one — a list-vs-detail
mismatch. The backend serves both from live DB and is consistent.

Set staleTime: 0 + refetchOnMount/refetchOnWindowFocus on the storefront
query so it revalidates on every visit and tab refocus, keeping the list
prices current.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:06:34 +00:00
272362cfdf fix(product): detail page price now matches list tiles + fresher feeds
Single-product page showed basePrice with no discount while list tiles
showed the cheapest variant's discounted price. Cause: the detail view
defaulted selectedColor/selectedSize to the first in-stock color and the
first in-stock size independently — a combo that often isn't a real
variant, so no variant matched and it fell back to basePrice / no sale.

- ProductDetailView: derive a `defaultVariant` (cheapest in-stock variant
  by final price, == the tile's min/discount price) and use it for the
  initial selection, the initial displayed price, and the fallback when no
  exact color/size combo is matched. A shared `pricingOf` helper applies
  the same discount rule everywhere. Detail price now matches the tile.
- use-home-hooks: drop staleTime 5m -> 60s on the For You / discounts /
  top-sellers feeds so price/discount edits show up promptly in lists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 19:55:11 +00:00
0293655fc1 first 2026-04-29 01:44:16 +03:30