diff --git a/apps/miniapp/src/routes/settings.tsx b/apps/miniapp/src/routes/settings.tsx
index 4e54ff9..d05d24b 100644
--- a/apps/miniapp/src/routes/settings.tsx
+++ b/apps/miniapp/src/routes/settings.tsx
@@ -20,7 +20,9 @@ import {
updateMiniAppMemberStatus,
promoteMiniAppMember,
approveMiniAppPendingMember,
- rejectMiniAppPendingMember
+ rejectMiniAppPendingMember,
+ upsertMiniAppUtilityCategory,
+ type MiniAppUtilityCategory
} from '../miniapp-api'
import { minorToMajorString } from '../lib/money'
@@ -48,6 +50,16 @@ export default function SettingsRoute() {
// ── Profile settings ─────────────────────────────
const [profileEditorOpen, setProfileEditorOpen] = createSignal(false)
+ // ── Utility categories ───────────────────────────
+ const [categoryEditorOpen, setCategoryEditorOpen] = createSignal(false)
+ const [editingCategorySlug, setEditingCategorySlug] = createSignal(null)
+ const [savingCategory, setSavingCategory] = createSignal(false)
+ const [categoryForm, setCategoryForm] = createSignal({
+ name: '',
+ sortOrder: 0,
+ isActive: true
+ })
+
// ── Billing settings form ────────────────────────
const [billingEditorOpen, setBillingEditorOpen] = createSignal(false)
const [savingSettings, setSavingSettings] = createSignal(false)
@@ -142,6 +154,60 @@ export default function SettingsRoute() {
}
}
+ // ── Utility Category Editing ─────────────────────
+ function openAddCategory() {
+ setEditingCategorySlug(null)
+ setCategoryForm({
+ name: '',
+ sortOrder: adminSettings()?.categories.length ?? 0,
+ isActive: true
+ })
+ setCategoryEditorOpen(true)
+ }
+
+ function openEditCategory(category: MiniAppUtilityCategory) {
+ setEditingCategorySlug(category.slug)
+ setCategoryForm({
+ name: category.name,
+ sortOrder: category.sortOrder,
+ isActive: category.isActive
+ })
+ setCategoryEditorOpen(true)
+ }
+
+ async function handleSaveCategory() {
+ const data = initData()
+ if (!data) return
+
+ setSavingCategory(true)
+ try {
+ const form = categoryForm()
+ const slug = editingCategorySlug()
+ const category = await upsertMiniAppUtilityCategory(data, {
+ ...(slug ? { slug } : {}),
+ name: form.name,
+ sortOrder: form.sortOrder,
+ isActive: form.isActive
+ })
+
+ // Update local state
+ setAdminSettings((prev) => {
+ if (!prev) return prev
+ const existing = prev.categories.find((c) => c.slug === category.slug)
+ if (existing) {
+ return {
+ ...prev,
+ categories: prev.categories.map((c) => (c.slug === category.slug ? category : c))
+ }
+ }
+ return { ...prev, categories: [...prev.categories, category] }
+ })
+ setCategoryEditorOpen(false)
+ } finally {
+ setSavingCategory(false)
+ }
+ }
+
// ── Member Editing ──────────────────────────────
const [editMemberId, setEditMemberId] = createSignal(null)
const [savingMember, setSavingMember] = createSignal(false)
@@ -447,6 +513,40 @@ export default function SettingsRoute() {
)}
+
+ {/* Utility Categories */}
+
+ {copy().utilityCategoriesBody}
}
+ >
+ {(categories) => (
+
+
+ {(category) => (
+
+ openEditCategory(category)}
+ >
+
+ {category.name}
+
+ {category.isActive ? copy().onLabel : copy().offLabel}
+
+
+
+
+ )}
+
+
+
+ )}
+
+
{/* ── Billing Settings Editor Modal ────────────── */}
@@ -862,6 +962,50 @@ export default function SettingsRoute() {
+
+ {/* ── Category Editor Modal ────────────── */}
+ setCategoryEditorOpen(false)}
+ footer={
+
+
+
+
+ }
+ >
+
+
+ setCategoryForm((f) => ({ ...f, name: e.currentTarget.value }))}
+ />
+
+
+
+
+
)
}