DEAD_CODE_CLEAN #6

Merged
shafi merged 30 commits from DEAD_CODE_CLEAN into main 2026-09-14 04:29:36 +00:00
79 changed files with 459 additions and 3108 deletions
Showing only changes of commit adb81a6b3d - Show all commits

View file

@ -32,7 +32,19 @@
"Shell(python3 -c import sys,json; d=json.load(sys.stdin); print('products:', len(d.get('products',[]))); print('tags:', [t['tagName'] for t in d.get('tags',[])][:12]) 2 >& 1)", "Shell(python3 -c import sys,json; d=json.load(sys.stdin); print('products:', len(d.get('products',[]))); print('tags:', [t['tagName'] for t in d.get('tags',[])][:12]) 2 >& 1)",
"Shell(curl -s http://localhost:4174/src/styles.css 2 > /dev/null)", "Shell(curl -s http://localhost:4174/src/styles.css 2 > /dev/null)",
"Shell(printf:*)", "Shell(printf:*)",
"Shell(cd /Users/mohammedshafiuddin/WebDev/freshyo/apps/backend && python3 - <<'EOF' import re with open('dumps/local_8_aug.sql') as f: content = f.read() # Find all CREATE TABLE positions and all REFERENCES target tables create_positions = {} # table name -> line for m in re.finditer(r'CREATE TABLE(?: IF NOT EXISTS)? \"?([A-Za-z_]+)\"? \\(', content): create_positions[m.group(1)] = content.count('\\n', 0, m.start()) + 1 # Verify each REFERENCES target is defined before the referencing CREATE errors = [] for m in re.finditer(r'CREATE TABLE(?: IF NOT EXISTS)? \"?([A-Za-z_]+)\"? \\(([^;]*?)\\)', content, re.S): table = m.group(1) table_line = content.count('\\n', 0, m.start()) + 1 body = m.group(2) for ref in re.findall(r'REFERENCES `?([A-Za-z_]+)`?\\(', body): if ref in create_positions and create_positions[ref] > table_line: errors.append(f\"{table} (line {table_line}) references {ref} (defined line {create_positions[ref]})\") if errors: print(\"FAIL - out of order:\") for e in errors: print(\" \", e) else: print(\"PASS - all REFERENCES resolve to tables defined earlier\") EOF)" "Shell(cd /Users/mohammedshafiuddin/WebDev/freshyo/apps/backend && python3 - <<'EOF' import re with open('dumps/local_8_aug.sql') as f: content = f.read() # Find all CREATE TABLE positions and all REFERENCES target tables create_positions = {} # table name -> line for m in re.finditer(r'CREATE TABLE(?: IF NOT EXISTS)? \"?([A-Za-z_]+)\"? \\(', content): create_positions[m.group(1)] = content.count('\\n', 0, m.start()) + 1 # Verify each REFERENCES target is defined before the referencing CREATE errors = [] for m in re.finditer(r'CREATE TABLE(?: IF NOT EXISTS)? \"?([A-Za-z_]+)\"? \\(([^;]*?)\\)', content, re.S): table = m.group(1) table_line = content.count('\\n', 0, m.start()) + 1 body = m.group(2) for ref in re.findall(r'REFERENCES `?([A-Za-z_]+)`?\\(', body): if ref in create_positions and create_positions[ref] > table_line: errors.append(f\"{table} (line {table_line}) references {ref} (defined line {create_positions[ref]})\") if errors: print(\"FAIL - out of order:\") for e in errors: print(\" \", e) else: print(\"PASS - all REFERENCES resolve to tables defined earlier\") EOF)",
"Shell(find packages/db_helper_sqlite/src packages/db_helper_postgres/src -name \"*.ts\" | xargs wc -l | tail -5 && echo \"=== apps imports of db_helper ===\" && grep -rn \"db_helper\" apps packages --include=\"*.ts\" --include=\"*.tsx\" --include=\"*.json\" -l | grep -v node_modules | grep -v \"db_helper_sqlite/\\|db_helper_postgres/\\|\\.turbo\\|dist\" | sort -u)",
"Shell(cat apps/backend/wrangler.toml 2>/dev/null | head -30; echo \"=== package.json ===\"; cat apps/backend/package.json; echo \"=== how build/dev resolves ===\"; ls apps/backend/.wrangler/tmp/dev-*/worker.js 2>/dev/null | head -1 | xargs grep -o \"sqliteService\\|getAllSlotsWithProductsForCache\" 2>/dev/null | head -3; echo \"=== tsx alias config? ===\"; grep -rn \"tsx\\|alias\" apps/backend/package.json apps/backend/wrangler.toml 2>/dev/null | head; echo \"=== node_modules links ===\"; ls -la apps/backend/node_modules/ 2>/dev/null | grep -i \"db_helper\\|sqlite\\|postgres\" | head)",
"Shell(cd /Users/mohammedshafiuddin/WebDev/freshyo && for pkg in packages/db_helper_sqlite packages/db_helper_postgres; do echo \"########## $pkg ##########\"; for f in $(find $pkg/src -name \"*.ts\" | sort); do # relative imports only grep -oE \"from '(\\.\\.?/[^']+)'\" $f | sed \"s/from '//;s/'//\" | while read imp; do resolved=$(cd $(dirname $f) && node -e \"const p=require('path');try{const r=p.resolve('$imp');console.log(r.startsWith(process.cwd())?p.relative(process.cwd(),r):'')}catch(e){}\" 2>/dev/null) if [ -n \"$resolved\" ] && [ -f \"$resolved.ts\" ] || [ -n \"$resolved\" ] && [ -f \"$resolved\" ]; then echo \"$(echo $f | sed 's|^packages/||') -> $resolved\"; fi done; done; done 2>/dev/null | sort -u | grep -v \"db_index\\|schema.ts ->\" | head -80)",
"Shell(node -e const fs = require(\"fs\"), path = require(\"path\"); for (const pkg of [\"packages/db_helper_sqlite\", \"packages/db_helper_postgres\"]) { console.log(\"########## \" + pkg + \" ##########\"); const files = []; (function walk(d){ for (const e of fs.readdirSync(d)) { const p = path.join(d,e); if (fs.statSync(p).isDirectory()) walk(p); else if (e.endsWith(\".ts\")) files.push(p); } })(pkg); const edges = {}; for (const f of files) { const src = fs.readFileSync(f, \"utf8\"); const re = /from\\s+[\\x27\"](\\.\\.?\\/[^\\x27\"]+)[\\x27\"]/g; let m; while ((m = re.exec(src))) { const base = path.dirname(f); let target = path.resolve(base, m[1]); if (!fs.existsSync(target) && fs.existsSync(target + \".ts\")) target += \".ts\"; if (target.startsWith(process.cwd()) && fs.existsSync(target)) { const rel = path.relative(pkg, target); if (!rel.startsWith(\"..\")) { (edges[f] = edges[f] || []).push(rel); } } } } const allRel = files.map(f => path.relative(pkg, f)); const imported = new Set(Object.values(edges).flat()); const deadFiles = allRel.filter(r => r !== \"index.ts\" && !imported.has(r) && !r.includes(\"/db/seed\") && !r.includes(\"drizzle.config\")); // internal cross-file usage counts: who imports this rel path (excluding itself) for (const f of files) { const rel = path.relative(pkg, f); const importers = Object.entries(edges).filter(([src, tgts]) => tgts.includes(rel) && src !== f).map(([src]) => path.relative(pkg, src)); if (rel.endsWith(\".ts\") && importers.length) console.log(rel + \" <== imported by: \" + importers.join(\", \")); } console.log(\"--- files with NO internal importers (candidates for dead file): ---\"); console.log(deadFiles.join(\"\\n\")); } )",
"Shell(node -e const fs = require(\"fs\"), path = require(\"path\"); for (const pkg of [\"packages/db_helper_sqlite\", \"packages/db_helper_postgres\"]) { console.log(\"########## \" + pkg + \" ##########\"); const files = []; (function walk(d){ if (d.includes(\"node_modules\")) return; for (const e of fs.readdirSync(d)) { const p = path.join(d,e); if (fs.statSync(p).isDirectory()) walk(p); else if (e.endsWith(\".ts\")) files.push(p); } })(pkg); const imported = new Set(); const importers = {}; // target -> [sources] for (const f of files) { const src = fs.readFileSync(f, \"utf8\"); const re = /from\\s+[\\x27\"](\\.\\.?\\/[^\\x27\"]+)[\\x27\"]/g; let m; while ((m = re.exec(src))) { let target = path.resolve(path.dirname(f), m[1]); if (!fs.existsSync(target) && fs.existsSync(target + \".ts\")) target += \".ts\"; if (target.startsWith(pkg) && fs.existsSync(target)) { const rel = path.relative(pkg, target); imported.add(rel); (importers[rel] = importers[rel] || []).push(path.relative(pkg, f)); } } } const excluded = new Set([\"index.ts\",\"drizzle.config.ts\",\"src/db/seed.ts\",\"src/db/db_index.ts\",\"src/db/schema.ts\"]); const noImporter = files.map(f=>path.relative(pkg,f)).filter(r => !imported.has(r) && !excluded.has(r) && !r.includes(\"drizzle.config\")); console.log(\"Files never imported internally (incl. index.ts not importing them? checked separately):\"); for (const r of noImporter) console.log(\" \" + r); // now check which of those are imported by index.ts explicitly const idx = fs.readFileSync(path.join(pkg,\"index.ts\"),\"utf8\"); for (const r of noImporter) { const basename = path.basename(r, \".ts\"); if (!idx.includes(basename)) console.log(\" [also NOT in index.ts] \" + r); } } )",
"Shell(node -e const fs = require(\"fs\"), path = require(\"path\"); const pkg = \"packages/db_helper_sqlite\"; const idx = fs.readFileSync(path.join(pkg,\"index.ts\"),\"utf8\"); const re = /from\\s+[\\x27\"](\\.\\.?\\/[^\\x27\"]+)[\\x27\"]/g; let m; while ((m = re.exec(idx))) { const raw = m[1]; let target = path.resolve(pkg, raw); if (!fs.existsSync(target) && fs.existsSync(target+\".ts\")) target += \".ts\"; console.log(JSON.stringify(raw), \"exists:\", fs.existsSync(target), \"rel:\", path.relative(pkg, target), \"startsWithPkg:\", target.startsWith(pkg), \"target:\", target); } )",
"Shell(node -e const fs = require(\"fs\"), path = require(\"path\"); const cwd = process.cwd(); for (const pkg of [\"packages/db_helper_sqlite\", \"packages/db_helper_postgres\"]) { console.log(\"########## \" + pkg + \" ##########\"); const pkgAbs = path.resolve(pkg); const files = []; (function walk(d){ if (d.includes(\"node_modules\")) return; for (const e of fs.readdirSync(d)) { const p = path.join(d,e); if (fs.statSync(p).isDirectory()) walk(p); else if (e.endsWith(\".ts\")) files.push(p); } })(pkgAbs); const imported = new Set(); for (const f of files) { const src = fs.readFileSync(f, \"utf8\"); const re = /from\\s+[\\x27\"](\\.\\.?\\/[^\\x27\"]+)[\\x27\"]/g; let m; while ((m = re.exec(src))) { let target = path.resolve(path.dirname(f), m[1]); if (!fs.existsSync(target) && fs.existsSync(target + \".ts\")) target += \".ts\"; if (target.startsWith(pkgAbs) && fs.existsSync(target) && target !== f) { imported.add(path.relative(pkgAbs, target)); } } } const allRel = files.map(f => path.relative(pkgAbs, f)).sort(); console.log(\"Total ts files:\", allRel.length); for (const r of allRel) { if (r === \"index.ts\" || r.includes(\"drizzle.config\")) continue; if (!imported.has(r)) console.log(\" NEVER-IMPORTED FILE: \" + r); } } )",
"Shell(for:*)",
"Shell(do:*)",
"Shell(done:*)",
"Shell(cd /Users/mohammedshafiuddin/WebDev/freshyo && echo \"=== product.ts trpc import line & usage count ===\" && sed -n '1,40p' apps/backend/src/trpc/apis/admin-apis/apis/product.ts && echo \"=== count occurrences of each beyond import ===\" && for fn in checkUnitExists getProductImagesById replaceProductTags; do echo \"$fn: total=$(grep -c \"$fn\" apps/backend/src/trpc/apis/admin-apis/apis/product.ts)\"; done)",
"Shell(node -e const fs = require(\"fs\"); const parse = (p) => { const src = fs.readFileSync(p,\"utf8\"); const names = new Set(); // export { a, b as c, ... } from / re-export lists const re = /\\bexport\\s*\\{([^}]*)\\}/g; let m; while ((m = re.exec(src))) { const body = m[1]; for (let line of body.split(\",\")) { line = line.trim().replace(/\\/\\/.*$/,\"\").trim(); if (!line) continue; const asMatch = line.match(/^(.+?)\\s+as\\s+(.+)$/); if (asMatch) names.add(asMatch[2].trim()); else if (line.match(/^[A-Za-z_$][\\w$]*$/)) names.add(line); } } return names; }; const sqlite = parse(\"packages/db_helper_sqlite/index.ts\"); const pg = parse(\"packages/db_helper_postgres/index.ts\"); const onlySqlite = [...sqlite].filter(x=>!pg.has(x)).sort(); const onlyPg = [...pg].filter(x=>!sqlite.has(x)).sort(); console.log(\"ONLY IN SQLITE INDEX:\", onlySqlite.join(\", \")); console.log(\"ONLY IN POSTGRES INDEX:\", onlyPg.join(\", \")); )"
], ],
"deny": [], "deny": [],
"defaultMode": "default" "defaultMode": "default"

View file

@ -26,7 +26,7 @@
- Wants feature parity maintained between the web-ui and user-ui apps (port logic/data patterns, rebuild UI per platform) — e.g., when he asks for a link in the cart page "at the coupons section" of @apps/user-ui/components/cart-page.tsx, he means mirror user-ui's coupons section (card, header, icon) in web-ui and add the requested link there; and conversely asks for features added in one app to be added to the sibling app "too" (e.g., "add the coupon redirection on the user-ui cart-page too"), expecting the agent to locate the equivalent component/route and replicate it. Confidence: 0.98 - Wants feature parity maintained between the web-ui and user-ui apps (port logic/data patterns, rebuild UI per platform) — e.g., when he asks for a link in the cart page "at the coupons section" of @apps/user-ui/components/cart-page.tsx, he means mirror user-ui's coupons section (card, header, icon) in web-ui and add the requested link there; and conversely asks for features added in one app to be added to the sibling app "too" (e.g., "add the coupon redirection on the user-ui cart-page too"), expecting the agent to locate the equivalent component/route and replicate it. Confidence: 0.98
- Wants the web-ui home page section/component order to match the reference user-ui home page exactly (e.g., "on the home page have the order same as that of @apps/user-ui/.../home/index.tsx"), including when deciding placement of sections like Explore, Stores, Slots, and All Products. Confidence: 0.7 - Wants the web-ui home page section/component order to match the reference user-ui home page exactly (e.g., "on the home page have the order same as that of @apps/user-ui/.../home/index.tsx"), including when deciding placement of sections like Explore, Stores, Slots, and All Products. Confidence: 0.7
- Wants brand/logo spots to use the single real brand logo asset (shared across apps, e.g., the Freshyo splash logo) displayed as an image, not a generic/decorative icon (e.g., a "meat piece" Meat/Beef icon) — web-ui should be on par with user-ui and show the same logo; applies to favicon, PWA icons, and every in-UI branding spot (sidebar, topbar, login, home banner). Confidence: 0.85 - Wants brand/logo spots to use the single real brand logo asset (shared across apps, e.g., the Freshyo splash logo) displayed as an image, not a generic/decorative icon (e.g., a "meat piece" Meat/Beef icon) — web-ui should be on par with user-ui and show the same logo; applies to favicon, PWA icons, and every in-UI branding spot (sidebar, topbar, login, home banner). Confidence: 0.85
- Prefers dead-code audits to be documented in a markdown file. Confidence: 0.8 - Prefers dead-code audits to be documented in a markdown file. Confidence: 0.9
- Prefers detailed technical documentation of system architecture, data models, flows, and integrations in markdown format. Confidence: 0.8 - Prefers detailed technical documentation of system architecture, data models, flows, and integrations in markdown format. Confidence: 0.8
- Wants edge cases explicitly enumerated when documenting or analyzing existing code/systems. Confidence: 0.8 - Wants edge cases explicitly enumerated when documenting or analyzing existing code/systems. Confidence: 0.8
- Values end-to-end analysis of features (architecture, data model, flows, integrations, and edge cases) when asked to explain how something works. Confidence: 0.7 - Values end-to-end analysis of features (architecture, data model, flows, integrations, and edge cases) when asked to explain how something works. Confidence: 0.7
@ -90,6 +90,9 @@ er-ui/app/(drawer)/(tabs)/home/index.tsx, I want similar here") and expects the
- For bulk/sequential generation from a code pattern (e.g., ABC12 → ABC13...), expects numbering to continue from the HIGHEST existing value so regeneration never re-emits already-entered codes — after entering ABC12, the next batch must start at ABC13, not restart at 12 (the first matching code). Confidence: 0.6 - For bulk/sequential generation from a code pattern (e.g., ABC12 → ABC13...), expects numbering to continue from the HIGHEST existing value so regeneration never re-emits already-entered codes — after entering ABC12, the next batch must start at ABC13, not restart at 12 (the first matching code). Confidence: 0.6
- Dislikes UI that renders transient loading states as definitive business states — e.g., the home page showed every item as "out of stock" until slot data loaded, because the out-of-stock check treated "no slot yet" (data not loaded) as genuinely out of stock. Expects a "data loaded" guard so the UI never flashes a wrong definitive status (out of stock) before data arrives, while still applying real out-of-stock detection once loaded. Confidence: 0.7 - Dislikes UI that renders transient loading states as definitive business states — e.g., the home page showed every item as "out of stock" until slot data loaded, because the out-of-stock check treated "no slot yet" (data not loaded) as genuinely out of stock. Expects a "data loaded" guard so the UI never flashes a wrong definitive status (out of stock) before data arrives, while still applying real out-of-stock detection once loaded. Confidence: 0.7
- When deleting a record, prefers the existing soft-delete/invalidate path over hard-deleting rows: the supported delete already sets `isInvalidated: true` (keeping history/audit and avoiding broken FK joins), and a raw hard delete should only be considered when confirmed nothing references the row (no usage records), with related rows (couponApplicableUsers/Products) removed transactionally to avoid orphans. Confidence: 0.75 - When deleting a record, prefers the existing soft-delete/invalidate path over hard-deleting rows: the supported delete already sets `isInvalidated: true` (keeping history/audit and avoiding broken FK joins), and a raw hard delete should only be considered when confirmed nothing references the row (no usage records), with related rows (couponApplicableUsers/Products) removed transactionally to avoid orphans. Confidence: 0.75
- When commissioning a dead-code audit of an app/package, expects a "close analysis" that is exhaustive across every category — dead, unreachable, and unused code: orphaned files, unreachable/unlinked routes, dormant or disabled-by-flag features, dead branches and commented-out blocks, and unused imports/exports — finding "every such code which serves no purpose" rather than only obviously-unreferenced files; findings should distinguish verified-dead items from merely dormant/judgment-call items. Confidence: 0.65
- When an analysis/audit has been produced for one app in the monorepo, expects the same analysis run for the sibling apps too — after the user-ui dead-code audit he immediately asked "now do the same for the @apps/admin-ui" — applying the identical exhaustive methodology and document style to each named app. Confidence: 0.85
- For per-app analysis documents, expects a consistent naming pattern following the established convention (`user_ui_dpsk.md``admin_ui_dpsk.md`, i.e., `{app}_dpsk.md` at the repo root) rather than an ad-hoc filename — "make another md file with same naming pattern". Confidence: 0.9
e changes. Confidence: 0.95 e changes. Confidence: 0.95
er-ui/app/(drawer)/(tabs)/home/index.tsx, I want similar here") and expects the agent to mirror that pattern exactly, including details like item counts and container styling. Confidence: 0.9 er-ui/app/(drawer)/(tabs)/home/index.tsx, I want similar here") and expects the agent to mirror that pattern exactly, including details like item counts and container styling. Confidence: 0.9
als) rather than introducing new brand palettes; approval of a redesign's layout/composition does not imply approval of color or theme changes. Confidence: 0.95 als) rather than introducing new brand palettes; approval of a redesign's layout/composition does not imply approval of color or theme changes. Confidence: 0.95

View file

@ -1,83 +0,0 @@
/** @type {Detox.DetoxConfig} */
module.exports = {
testRunner: {
args: {
'$0': 'jest',
config: 'e2e/jest.config.js'
},
jest: {
setupTimeout: 120000
}
},
apps: {
'ios.debug': {
type: 'ios.app',
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/YOUR_APP.app',
build: 'xcodebuild -workspace ios/YOUR_APP.xcworkspace -scheme YOUR_APP -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build'
},
'ios.release': {
type: 'ios.app',
binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/YOUR_APP.app',
build: 'xcodebuild -workspace ios/YOUR_APP.xcworkspace -scheme YOUR_APP -configuration Release -sdk iphonesimulator -derivedDataPath ios/build'
},
'android.debug': {
type: 'android.apk',
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
reversePorts: [
8081
]
},
'android.release': {
type: 'android.apk',
binaryPath: 'android/app/build/outputs/apk/release/app-release.apk',
build: 'cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release'
}
},
devices: {
simulator: {
type: 'ios.simulator',
device: {
type: 'iPhone 15'
}
},
attached: {
type: 'android.attached',
device: {
adbName: '.*'
}
},
emulator: {
type: 'android.emulator',
device: {
avdName: 'Pixel_3a_API_30_x86'
}
}
},
configurations: {
'ios.sim.debug': {
device: 'simulator',
app: 'ios.debug'
},
'ios.sim.release': {
device: 'simulator',
app: 'ios.release'
},
'android.att.debug': {
device: 'attached',
app: 'android.debug'
},
'android.att.release': {
device: 'attached',
app: 'android.release'
},
'android.emu.debug': {
device: 'emulator',
app: 'android.debug'
},
'android.emu.release': {
device: 'emulator',
app: 'android.release'
}
}
};

File diff suppressed because one or more lines are too long

View file

@ -1,248 +0,0 @@
import React, { useState, useCallback, useMemo } from 'react';
import { View, TouchableOpacity, Alert, RefreshControl } from 'react-native';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import { tw, MyButton, MyText, SearchBar, MyFlatList, useMarkDataFetchers, MyTouchableOpacity, BottomDropdown } from 'common-ui';
import useManualRefresh from 'common-ui/hooks/useManualRefresh';
import { trpc } from '@/src/trpc-client';
import { useRouter } from 'expo-router';
import { useInfiniteQuery } from '@tanstack/react-query';
const ReservedCouponItem = ({ item }: { item: any }) => {
const getStatus = () => {
if (item.isRedeemed) return 'redeemed';
if (item.validTill && new Date(item.validTill) <= new Date()) return 'expired';
return 'active';
};
const status = getStatus();
const getBorderColor = () => {
if (status === 'active') return 'border-green-500';
if (status === 'expired') return 'border-yellow-500';
return 'border-blue-500';
};
const getBgColor = () => {
if (status === 'active') return 'bg-green-100';
if (status === 'expired') return 'bg-yellow-100';
return 'bg-blue-100';
};
const getTextColor = () => {
if (status === 'active') return 'text-green-600';
if (status === 'expired') return 'text-yellow-600';
return 'text-blue-600';
};
const getIconColor = () => {
if (status === 'active') return '#10b981';
if (status === 'expired') return '#f59e0b';
return '#3b82f6';
};
return (
<View style={tw`bg-white p-4 mb-4 rounded-2xl shadow-lg border-l-4 ${getBorderColor()}`}>
<View style={tw`flex-row items-center mb-3`}>
<View style={tw`w-10 h-10 rounded-full ${getBgColor()} items-center justify-center mr-3`}>
<MaterialCommunityIcons
name={item.discountPercent ? "percent" : "currency-inr"}
size={20}
color={getIconColor()}
/>
</View>
<View style={tw`flex-1`}>
<MyText style={tw`text-lg font-bold text-gray-800`}>{item.secretCode}</MyText>
<MyText style={tw`text-sm text-gray-500`}>Coupon: {item.couponCode}</MyText>
</View>
<View style={tw`px-2 py-1 rounded-full ${getBgColor()}`}>
<MyText style={tw`text-xs font-semibold ${getTextColor()}`}>
{status === 'active' ? 'Active' : status === 'expired' ? 'Expired' : 'Redeemed'}
</MyText>
</View>
</View>
<View style={tw`bg-gray-50 p-3 rounded-lg mb-3`}>
<MyText style={tw`text-base font-semibold mb-1 text-gray-800`}>
Discount: {item.discountPercent ? `${item.discountPercent}% off` : item.flatDiscount ? `${item.flatDiscount} off` : 'N/A'}
</MyText>
<View style={tw`flex-row justify-between`}>
<MyText style={tw`text-sm text-gray-600`}>Min Order: {item.minOrder ? `${item.minOrder}` : 'None'}</MyText>
<MyText style={tw`text-sm text-gray-600`}>Max: {item.maxValue ? `${item.maxValue}` : 'None'}</MyText>
</View>
<MyText style={tw`text-sm text-gray-600 mt-1`}>
Valid Till: {item.validTill ? new Date(item.validTill).toLocaleDateString() : 'No expiry'}
</MyText>
</View>
{item.isRedeemed && item.redeemedUser && (
<View style={tw`mb-3`}>
<MyText style={tw`text-sm text-gray-700`}>
<MaterialCommunityIcons name="account-check" size={14} color="#6b7280" /> Redeemed by: {item.redeemedUser.name || 'Unknown'} ({item.redeemedUser.mobile})
</MyText>
<MyText style={tw`text-sm text-gray-600`}>
Redeemed on: {item.redeemedAt ? new Date(item.redeemedAt).toLocaleDateString() : 'N/A'}
</MyText>
</View>
)}
<View style={tw`flex-row justify-between items-center mt-3`}>
<MyText style={tw`text-sm text-gray-700`}>
<MaterialCommunityIcons name="account" size={14} color="#6b7280" /> Created by: {item.creator?.name || 'Unknown'}
</MyText>
</View>
</View>
);
};
export default function ReservedCoupons() {
const router = useRouter();
const [refreshing, setRefreshing] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
const [statusFilters, setStatusFilters] = useState<string[]>([]);
const {
data,
fetchNextPage,
hasNextPage,
isLoading,
isFetchingNextPage,
refetch,
} = trpc.admin.coupon.getReservedCoupons.useInfiniteQuery(
{ limit: 50 },
{
getNextPageParam: (lastPage) => lastPage.nextCursor,
}
);
const coupons = data?.pages.flatMap(page => page.coupons) || [];
const getStatus = (coupon: any) => {
if (coupon.isRedeemed) return 'redeemed';
if (coupon.validTill && new Date(coupon.validTill) <= new Date()) return 'expired';
return 'active';
};
const filteredCoupons = useMemo(() => {
let filtered = coupons.filter(coupon =>
coupon.secretCode.toLowerCase().includes(searchQuery.toLowerCase()) ||
coupon.couponCode.toLowerCase().includes(searchQuery.toLowerCase())
);
if (statusFilters.length > 0) {
filtered = filtered.filter(coupon => statusFilters.includes(getStatus(coupon)));
}
return filtered;
}, [coupons, searchQuery, statusFilters]);
const handleRefresh = useCallback(async () => {
setRefreshing(true);
await refetch();
setRefreshing(false);
}, [refetch]);
useManualRefresh(() => refetch());
useMarkDataFetchers(() => {
refetch();
});
if (isLoading) {
return (
<View style={tw`flex-1 justify-center items-center bg-white`}>
<View style={tw`w-16 h-16 bg-blue-100 rounded-full items-center justify-center mb-4`}>
<MaterialCommunityIcons name="loading" size={32} color="#3b82f6" />
</View>
<MyText style={tw`text-lg font-semibold text-gray-600`}>Loading Reserved Coupons...</MyText>
</View>
);
}
return (
<View style={tw`flex-1 bg-white`}>
<View style={tw`flex-row items-center px-4 py-2`}>
<View style={tw`flex-1 mr-2`}>
<SearchBar
value={searchQuery}
onChangeText={setSearchQuery}
placeholder="Search reserved coupons..."
/>
</View>
<BottomDropdown
label="Filter by Status"
value={statusFilters}
options={[
{ label: 'Active', value: 'active' },
{ label: 'Expired', value: 'expired' },
{ label: 'Redeemed', value: 'redeemed' },
]}
onValueChange={(value) => setStatusFilters(value as string[])}
multiple={true}
triggerComponent={({ onPress }) => (
<TouchableOpacity onPress={onPress} style={tw`p-2`}>
<MaterialCommunityIcons name="filter-variant" size={24} color="#6b7280" />
</TouchableOpacity>
)}
/>
</View>
<MyFlatList
data={filteredCoupons}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => <ReservedCouponItem item={item} />}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
}
contentContainerStyle={tw`px-4 pb-4`}
onEndReached={() => {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}}
onEndReachedThreshold={0.5}
ListEmptyComponent={
searchQuery ? (
<View style={tw`flex-1 justify-center items-center py-20`}>
<View style={tw`w-20 h-20 bg-gray-100 rounded-full items-center justify-center mb-4`}>
<MaterialCommunityIcons name="magnify" size={40} color="#9ca3af" />
</View>
<MyText style={tw`text-xl font-semibold text-gray-600 mb-2`}>No Results</MyText>
<MyText style={tw`text-gray-500 text-center mb-4`}>No reserved coupons match &ldquo;{searchQuery}&rdquo;</MyText>
<MyButton onPress={() => setSearchQuery('')} style={tw`bg-gray-500`}>
<MyText style={tw`text-white font-semibold`}>Clear Search</MyText>
</MyButton>
</View>
) : (
<View style={tw`flex-1 justify-center items-center py-20`}>
<View style={tw`w-20 h-20 bg-gray-100 rounded-full items-center justify-center mb-4`}>
<MaterialCommunityIcons name="ticket-percent-outline" size={40} color="#9ca3af" />
</View>
<MyText style={tw`text-xl font-semibold text-gray-600 mb-2`}>No Reserved Coupons Yet</MyText>
<MyText style={tw`text-gray-500 text-center mb-4`}>Create your first reserved coupon to start offering secret discounts</MyText>
<MyButton onPress={() => router.push('/(drawer)/dashboard/coupons/create')} style={tw`bg-blue-500`}>
<View style={tw`flex-row items-center`}>
<MaterialCommunityIcons name="plus" size={16} color="white" />
<MyText style={tw`text-white font-semibold ml-1`}>Create Reserved Coupon</MyText>
</View>
</MyButton>
</View>
)
}
/>
{/* FAB for Add New Reserved Coupon */}
<MyTouchableOpacity
onPress={() => router.push('/(drawer)/dashboard/coupons/create')}
activeOpacity={0.95}
style={{ position: 'absolute', bottom: 32, right: 24, zIndex: 100 }}
>
<LinearGradient
colors={['#F83758', '#E91E63']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={tw`w-16 h-16 rounded-[24px] items-center justify-center shadow-lg shadow-pink300`}
>
<MaterialCommunityIcons name="plus" size={32} color="white" />
</LinearGradient>
</MyTouchableOpacity>
</View>
);
}

View file

@ -3,7 +3,6 @@ import { View, ScrollView, Pressable } from 'react-native';
import { useRouter } from 'expo-router'; import { useRouter } from 'expo-router';
import MaterialIcons from '@expo/vector-icons/MaterialIcons'; import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { MyText, tw } from 'common-ui'; import { MyText, tw } from 'common-ui';
import { LinearGradient } from 'expo-linear-gradient';
import { theme } from 'common-ui/src/theme'; import { theme } from 'common-ui/src/theme';
import { trpc } from '@/src/trpc-client'; import { trpc } from '@/src/trpc-client';
import { useNavigationTarget } from 'common-ui/hooks/useNavigationTarget'; import { useNavigationTarget } from 'common-ui/hooks/useNavigationTarget';

View file

@ -537,11 +537,6 @@ export default function DeliverySequences() {
</View> </View>
) : ( ) : (
<View style={tw`flex-1`}> <View style={tw`flex-1`}>
{/* <View style={tw`bg-blue-50 px-4 py-2 mb-2`}>
<MyText style={tw`text-blue-700 text-xs text-center`}>
Long press an item to drag and reorder
</MyText>
</View> */}
<DraggableFlatList <DraggableFlatList
data={localOrderedOrders} data={localOrderedOrders}
renderItem={({ item, drag, isActive }) => ( renderItem={({ item, drag, isActive }) => (

View file

@ -14,11 +14,8 @@ import {
tw, tw,
MyTextInput, MyTextInput,
BottomDropdown, BottomDropdown,
ImageUploader,
} from 'common-ui'; } from 'common-ui';
import { trpc } from '@/src/trpc-client'; import { trpc } from '@/src/trpc-client';
import usePickImage from 'common-ui/src/components/use-pick-image';
import { useUploadToObjectStorage } from '@/hooks/useUploadToObjectStore';
interface User { interface User {
id: number; id: number;
@ -32,8 +29,6 @@ export default function SendNotifications() {
const [selectedUserIds, setSelectedUserIds] = useState<number[]>([]); const [selectedUserIds, setSelectedUserIds] = useState<number[]>([]);
const [title, setTitle] = useState(''); const [title, setTitle] = useState('');
const [message, setMessage] = useState(''); const [message, setMessage] = useState('');
const [selectedImage, setSelectedImage] = useState<{ blob: Blob; mimeType: string } | null>(null);
const [displayImage, setDisplayImage] = useState<{ uri?: string } | null>(null);
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
// Query users eligible for notifications // Query users eligible for notifications
@ -41,8 +36,6 @@ export default function SendNotifications() {
search: searchQuery, search: searchQuery,
}); });
const { uploadSingle } = useUploadToObjectStorage();
// Send notification mutation // Send notification mutation
const sendNotification = trpc.admin.user.sendNotification.useMutation({ const sendNotification = trpc.admin.user.sendNotification.useMutation({
onSuccess: () => { onSuccess: () => {
@ -51,8 +44,6 @@ export default function SendNotifications() {
setSelectedUserIds([]); setSelectedUserIds([]);
setTitle(''); setTitle('');
setMessage(''); setMessage('');
setSelectedImage(null);
setDisplayImage(null);
}, },
onError: (error: any) => { onError: (error: any) => {
Alert.alert('Error', error.message || 'Failed to send notification'); Alert.alert('Error', error.message || 'Failed to send notification');
@ -66,29 +57,6 @@ export default function SendNotifications() {
value: user.id, value: user.id,
})); }));
const handleImagePick = usePickImage({
setFile: async (assets: any) => {
if (!assets || (Array.isArray(assets) && assets.length === 0)) {
setSelectedImage(null);
setDisplayImage(null);
return;
}
const file = Array.isArray(assets) ? assets[0] : assets;
const response = await fetch(file.uri);
const blob = await response.blob();
setSelectedImage({ blob, mimeType: file.mimeType || 'image/jpeg' });
setDisplayImage({ uri: file.uri });
},
multiple: false,
});
const handleRemoveImage = () => {
setSelectedImage(null);
setDisplayImage(null);
};
const handleSend = async () => { const handleSend = async () => {
if (title.trim().length === 0) { if (title.trim().length === 0) {
Alert.alert('Error', 'Please enter a title'); Alert.alert('Error', 'Please enter a title');
@ -117,20 +85,11 @@ export default function SendNotifications() {
} }
try { try {
let imageUrl: string | undefined;
// Upload image if selected
if (selectedImage) {
const { key } = await uploadSingle(selectedImage.blob, selectedImage.mimeType, 'notification');
imageUrl = key;
}
// Send notification // Send notification
await sendNotification.mutateAsync({ await sendNotification.mutateAsync({
userIds: selectedUserIds, userIds: selectedUserIds,
title: title.trim(), title: title.trim(),
text: message.trim(), text: message.trim(),
imageUrl,
}); });
} catch (error: any) { } catch (error: any) {
Alert.alert('Error', error.message || 'Failed to send notification'); Alert.alert('Error', error.message || 'Failed to send notification');
@ -196,17 +155,6 @@ export default function SendNotifications() {
/> />
</View> </View>
{/* Image Upload - Hidden for now */}
{/* <View style={tw`bg-white rounded-xl border border-gray-100 p-4 mb-4 shadow-sm`}>
<MyText style={tw`text-base font-bold text-gray-900 mb-3`}>Image (Optional)</MyText>
<ImageUploader
images={displayImage ? [displayImage] : []}
existingImageUrls={[]}
onAddImage={handleImagePick}
onRemoveImage={handleRemoveImage}
/>
</View> */}
{/* User Selection */} {/* User Selection */}
<View style={tw`bg-white rounded-xl border border-gray-100 p-4 mb-4 shadow-sm`}> <View style={tw`bg-white rounded-xl border border-gray-100 p-4 mb-4 shadow-sm`}>
<MyText style={tw`text-base font-bold text-gray-900 mb-3`}>Select Users (Optional)</MyText> <MyText style={tw`text-base font-bold text-gray-900 mb-3`}>Select Users (Optional)</MyText>

View file

@ -1,64 +0,0 @@
import React from 'react'
import { Formik } from 'formik'
import * as Yup from 'yup'
import { View, Text, TouchableOpacity } from 'react-native'
import { MyTextInput, BottomDropdown, tw } from 'common-ui'
import { trpc } from '@/src/trpc-client'
interface AddressPlaceFormProps {
onSubmit: (values: { placeName: string; zoneId: number | null }) => void
onClose: () => void
}
const AddressPlaceForm: React.FC<AddressPlaceFormProps> = ({ onSubmit, onClose }) => {
const { data: zones } = trpc.admin.address.getZones.useQuery()
const validationSchema = Yup.object({
placeName: Yup.string().required('Place name is required'),
zoneId: Yup.number().optional(),
})
const zoneOptions = zones?.map(z => ({ label: z.zoneName, value: z.id })) || []
return (
<View style={tw`p-4`}>
<Text style={tw`text-lg font-semibold mb-4`}>Add Place</Text>
<Formik
initialValues={{ placeName: '', zoneId: null as number | null }}
validationSchema={validationSchema}
onSubmit={(values) => {
onSubmit(values)
onClose()
}}
>
{({ handleChange, setFieldValue, handleSubmit, values, errors, touched }) => (
<View>
<MyTextInput
label="Place Name"
value={values.placeName}
onChangeText={handleChange('placeName')}
error={!!(touched.placeName && errors.placeName)}
/>
<BottomDropdown
label="Zone (Optional)"
value={values.zoneId as any}
options={zoneOptions}
onValueChange={(value) => setFieldValue('zoneId', value as number | undefined)}
placeholder="Select Zone"
/>
<View style={tw`flex-row justify-between mt-4`}>
<TouchableOpacity style={tw`bg-gray2 px-4 py-2 rounded`} onPress={onClose}>
<Text style={tw`text-gray-900`}>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity style={tw`bg-blue1 px-4 py-2 rounded`} onPress={() => handleSubmit()}>
<Text style={tw`text-white`}>Create</Text>
</TouchableOpacity>
</View>
</View>
)}
</Formik>
</View>
)
}
export default AddressPlaceForm

View file

@ -1,51 +0,0 @@
import React from 'react'
import { Formik } from 'formik'
import * as Yup from 'yup'
import { View, Text, TouchableOpacity } from 'react-native'
import { MyTextInput, tw } from 'common-ui'
interface AddressZoneFormProps {
onSubmit: (values: { zoneName: string }) => void
onClose: () => void
}
const AddressZoneForm: React.FC<AddressZoneFormProps> = ({ onSubmit, onClose }) => {
const validationSchema = Yup.object({
zoneName: Yup.string().required('Zone name is required'),
})
return (
<View style={tw`p-4`}>
<Text style={tw`text-lg font-semibold mb-4`}>Add Zone</Text>
<Formik
initialValues={{ zoneName: '' }}
validationSchema={validationSchema}
onSubmit={(values) => {
onSubmit(values)
onClose()
}}
>
{({ handleChange, handleSubmit, values, errors, touched }) => (
<View>
<MyTextInput
label="Zone Name"
value={values.zoneName}
onChangeText={handleChange('zoneName')}
error={!!(touched.zoneName && errors.zoneName)}
/>
<View style={tw`flex-row justify-between mt-4`}>
<TouchableOpacity style={tw`bg-gray2 px-4 py-2 rounded`} onPress={onClose}>
<Text style={tw`text-gray-900`}>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity style={tw`bg-blue1 px-4 py-2 rounded`} onPress={() => handleSubmit()}>
<Text style={tw`text-white`}>Create</Text>
</TouchableOpacity>
</View>
</View>
)}
</Formik>
</View>
)
}
export default AddressZoneForm

View file

@ -1,45 +0,0 @@
import React from "react";
import { ScrollView, View, StyleSheet } from "react-native";
import { ImageViewerURI } from "common-ui";
interface HorizontalImageScrollerProps {
urls: string[];
imageHeight?: number;
imageWidth?: number;
}
const HorizontalImageScroller: React.FC<HorizontalImageScrollerProps> = ({
urls,
imageHeight = 128,
imageWidth = 128,
}) => {
if (!urls || urls.length === 0) return null;
return (
<View style={styles.container}>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
{urls.map((url, idx) => (
<View key={idx} style={{ marginRight: 12 }}>
<ImageViewerURI
uri={url}
style={{
height: imageHeight,
width: imageWidth,
borderRadius: 12,
}}
/>
</View>
))}
</ScrollView>
</View>
);
};
const styles = StyleSheet.create({
container: {
marginVertical: 8,
},
});
export default HorizontalImageScroller;

View file

@ -1,4 +1,4 @@
import React, { forwardRef, useState, useEffect, useMemo } from 'react'; import React, { useState, useEffect, useMemo } from 'react';
import { View, TouchableOpacity, Alert } from 'react-native'; import { View, TouchableOpacity, Alert } from 'react-native';
import { Formik } from 'formik'; import { Formik } from 'formik';
import * as Yup from 'yup'; import * as Yup from 'yup';
@ -16,10 +16,6 @@ export interface StoreFormData {
products: number[]; products: number[];
} }
export interface StoreFormRef {
// Add methods if needed
}
interface StoreFormProps { interface StoreFormProps {
mode: 'create' | 'edit'; mode: 'create' | 'edit';
initialValues: StoreFormData; initialValues: StoreFormData;
@ -36,8 +32,7 @@ const validationSchema = Yup.object().shape({
products: Yup.array().of(Yup.number()), products: Yup.array().of(Yup.number()),
}); });
const StoreForm = forwardRef<StoreFormRef, StoreFormProps>((props, ref) => { function StoreForm({ mode, initialValues, onSubmit, isLoading, storeId }: StoreFormProps) {
const { mode, initialValues, onSubmit, isLoading, storeId } = props;
const { data: staffData } = trpc.admin.staffUser.getStaff.useQuery(); const { data: staffData } = trpc.admin.staffUser.getStaff.useQuery();
const { data: productsData } = trpc.admin.product.getProducts.useQuery(); const { data: productsData } = trpc.admin.product.getProducts.useQuery();
@ -199,8 +194,6 @@ const StoreForm = forwardRef<StoreFormRef, StoreFormProps>((props, ref) => {
}} }}
</Formik> </Formik>
); );
}); }
StoreForm.displayName = 'StoreForm';
export default StoreForm; export default StoreForm;

View file

@ -1,37 +0,0 @@
import React from 'react';
import { View, TouchableOpacity } from 'react-native';
import { MyText , tw } from "common-ui";
interface TabNavigationProps {
tabs: { key: string; title: string }[];
activeTab: string;
onTabChange: (tabKey: string) => void;
}
const TabNavigation: React.FC<TabNavigationProps> = ({ tabs, activeTab, onTabChange }) => {
return (
<View style={tw`flex-row bg-gray-100 rounded-xl p-1 mb-4`}>
{tabs.map((tab) => (
<TouchableOpacity
key={tab.key}
style={[
tw`flex-1 py-3 px-4 rounded-xl`,
activeTab === tab.key ? tw`bg-white shadow` : tw``
]}
onPress={() => onTabChange(tab.key)}
>
<MyText
style={[
tw`text-center font-medium`,
activeTab === tab.key ? tw`text-blue-600` : tw`text-gray-500`
]}
>
{tab.title}
</MyText>
</TouchableOpacity>
))}
</View>
);
};
export default TabNavigation;

View file

@ -1,109 +0,0 @@
import React, { useState } from 'react';
import { View, TouchableOpacity } from 'react-native';
import { MyText, tw, BottomDialog, MyTextInput } from 'common-ui';
import { trpc } from '@/src/trpc-client';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { Alert } from 'react-native';
interface UserIncidentDialogProps {
orderId: number;
userId: number;
open: boolean;
onClose: () => void;
onSuccess?: () => void;
}
export default function UserIncidentDialog({ orderId, userId, open, onClose, onSuccess }: UserIncidentDialogProps) {
const [adminComment, setAdminComment] = useState('');
const [negativityScore, setNegativityScore] = useState('');
const addIncidentMutation = trpc.admin.user.addUserIncident.useMutation({
onSuccess: () => {
Alert.alert('Success', 'Incident added successfully');
setAdminComment('');
setNegativityScore('');
onClose();
onSuccess?.();
},
onError: (error: any) => {
Alert.alert('Error', error.message || 'Failed to add incident');
},
});
const handleAddIncident = () => {
const score = negativityScore ? parseInt(negativityScore) : undefined;
if (!adminComment.trim() && !negativityScore) {
Alert.alert('Error', 'Please enter a comment or negativity score');
return;
}
addIncidentMutation.mutate({
userId,
orderId,
adminComment: adminComment || undefined,
negativityScore: score,
});
};
return (
<BottomDialog open={open} onClose={onClose}>
<View style={tw`p-6`}>
<View style={tw`items-center mb-6`}>
<View style={tw`w-12 h-12 bg-amber-100 rounded-full items-center justify-center mb-3`}>
<MaterialIcons name="warning" size={24} color="#D97706" />
</View>
<MyText style={tw`text-xl font-bold text-gray-900 text-center`}>
Add User Incident
</MyText>
<MyText style={tw`text-gray-500 text-center mt-2 text-sm leading-5`}>
Record an incident for this user. This will be visible in their profile.
</MyText>
</View>
<MyTextInput
topLabel="Admin Comment"
value={adminComment}
onChangeText={setAdminComment}
placeholder="Enter details about the incident..."
multiline
style={tw`h-24`}
/>
<MyTextInput
topLabel="Negativity Score (Optional)"
value={negativityScore}
onChangeText={setNegativityScore}
placeholder="0"
keyboardType="numeric"
style={tw`mt-4`}
/>
<View style={tw`bg-amber-50 p-4 rounded-xl border border-amber-100 mb-6 mt-4 flex-row items-start`}>
<MaterialIcons name="info-outline" size={20} color="#D97706" style={tw`mt-0.5`} />
<MyText style={tw`text-sm text-amber-800 ml-2 flex-1 leading-5`}>
Higher negativity scores indicate more serious incidents (e.g., repeated cancellations, abusive behavior).
</MyText>
</View>
<View style={tw`flex-row gap-3`}>
<TouchableOpacity
style={tw`flex-1 bg-gray-100 py-3.5 rounded-xl items-center`}
onPress={onClose}
>
<MyText style={tw`text-gray-700 font-bold`}>Cancel</MyText>
</TouchableOpacity>
<TouchableOpacity
style={tw`flex-1 bg-amber-500 py-3.5 rounded-xl items-center shadow-sm ${addIncidentMutation.isPending ? 'opacity-50' : ''}`}
onPress={handleAddIncident}
disabled={addIncidentMutation.isPending}
>
<MyText style={tw`text-white font-bold`}>
{addIncidentMutation.isPending ? 'Adding...' : 'Add Incident'}
</MyText>
</TouchableOpacity>
</View>
</View>
</BottomDialog>
);
}

View file

@ -1,3 +0,0 @@
import { AppContainer } from "common-ui";
export default AppContainer;

View file

@ -1,270 +0,0 @@
// import React, {
// createContext,
// useContext,
// useEffect,
// useState,
// ReactNode,
// } from "react";
// import {
// getJWT,
// deleteJWT,
// getRoles,
// saveJWT,
// saveRoles,
// saveUserId,
// getUserId,
// } from "../../hooks/useJWT";
// import { useFocusEffect, usePathname, useRouter } from "expo-router";
// import queryClient from "@/utils/queryClient";
// import { DeviceEventEmitter } from "react-native";
// import { FORCE_LOGOUT_EVENT, SESSION_EXPIRED_MSG } from "common-ui/src/lib/const-strs";
// import { useLogin, useLogout } from "@/api-hooks/auth.api";
// import {
// useUserResponsibilities,
// UserResponsibilities,
// } from "@/api-hooks/user.api";
// import { InfoToast, SuccessToast } from "@/services/toaster";
// import { useNotification } from "@/services/notif-service/notif-context";
// interface LoginFormInputs {
// login: string;
// password: string;
// useUsername?: boolean;
// expoPushToken?: string | null;
// }
// interface AuthContextType {
// isLoggedIn: boolean;
// setIsLoggedIn: (value: boolean) => void;
// logout: ({
// isSessionExpired,
// }: {
// isSessionExpired?: boolean;
// }) => Promise<void>;
// responsibilities: UserResponsibilities | null;
// responsibilitiesLoading: boolean;
// responsibilitiesError: Error | null;
// roles: string[] | null;
// setRoles: (roles: string[] | null) => void;
// refreshRoles: () => Promise<void>;
// loginFunc: (payload: LoginFormInputs) => Promise<void>;
// userId: number | null;
// isLoggingIn: boolean;
// loginError?: string;
// }
// const defaultResponsibilities: UserResponsibilities = {
// hospitalAdminFor: null,
// secretaryFor: [],
// };
// export const AuthContext = createContext<AuthContextType | undefined>(
// undefined
// );
// export const AuthProvider = ({ children }: { children: ReactNode }) => {
// const { mutate: loginApi, isPending: isLoggingIn, error: loginError } = useLogin();
// const [isLoggedIn, setIsLoggedIn] = useState(false);
// const [roles, setRoles] = useState<string[] | null>(null);
// const [userId, setUserId] = useState<number | null>(null);
// const refreshRoles = async () => {
// const r = await getRoles();
// setRoles(r);
// };
// useEffect(() => {
// refreshRoles();
// }, []);
// const { mutate: logoutApi } = useLogout();
// const router = useRouter();
// const [responsibilitiesError, setResponsibilitiesError] =
// useState<Error | null>(null);
// const {
// data: responsibilities,
// isLoading: responsibilitiesLoading,
// isFetching: responsibilitiesFetching,
// refetch: refetchResponsibilities,
// error: queryError,
// } = useUserResponsibilities(userId);
// React.useEffect(() => {
// (async () => {
// const token = await getJWT();
// setIsLoggedIn(!!token);
// if (!token) {
// if (!pathname.includes("login")) {
// router.replace("/login" as any);
// }
// } else {
// refetchResponsibilities();
// router.replace("/(drawer)/dashboard");
// const userId = await getUserId();
// setUserId(userId ? parseInt(userId) : null);
// }
// })();
// }, []);
// const pathname = usePathname();
// const logout = async ({
// isSessionExpired,
// }: {
// isSessionExpired?: boolean;
// }) => {
// const pageConditon =
// pathname.includes("/login") ||
// pathname.includes("/signup") ||
// pathname === "/";
// if (!isSessionExpired) {
// logoutApi({} as any, {
// onSuccess: () => {},
// onSettled: () => {
// if (!pageConditon) {
// router.replace({
// pathname: "/login" as any,
// params: isSessionExpired ? { message: SESSION_EXPIRED_MSG } : {},
// });
// }
// deleteJWT();
// },
// });
// setIsLoggedIn(false);
// } else {
// deleteJWT();
// setIsLoggedIn(false);
// InfoToast("Session expired. Please log in again.");
// if (!pageConditon) {
// router.replace({
// pathname: "/login" as any,
// params: { message: SESSION_EXPIRED_MSG },
// });
// }
// }
// queryClient.clear();
// };
// const loginFunc = async (data: LoginFormInputs) => {
// loginApi(data, {
// onSuccess: async (result) => {
// // refetchUserId();
// // await refetchUserData();
// await saveUserId(result.user.id.toString());
// setUserId(result.user.id);
// await saveJWT(result.token);
// // Update login state in auth context
// setIsLoggedIn(true);
// // Handle roles if available
// if (result.user.roles) {
// await saveRoles(result.user.roles);
// await refreshRoles();
// }
// await refetchResponsibilities();
// // Clear the 'message' search param from the URL after login
// router.replace({
// pathname: "/(drawer)/dashboard",
// params: {},
// });
// },
// onError: (e: any) => {
// // setError("login", {
// // type: "manual",
// // message: e.message || "Login failed",
// // });
// },
// });
// };
// React.useEffect(() => {
// const subscription = DeviceEventEmitter.addListener(
// FORCE_LOGOUT_EVENT,
// () => {
// logout({ isSessionExpired: true });
// }
// );
// return () => {
// subscription.remove();
// };
// }, []);
// return (
// <AuthContext.Provider
// value={{
// isLoggedIn,
// setIsLoggedIn,
// logout,
// responsibilities: responsibilities || defaultResponsibilities,
// responsibilitiesLoading,
// responsibilitiesError,
// roles,
// setRoles,
// refreshRoles,
// loginFunc,
// userId,
// isLoggingIn,
// loginError: loginError?.message
// }}
// >
// {children}
// </AuthContext.Provider>
// );
// };
// export const useAuth = () => {
// const context = useContext(AuthContext);
// if (!context) {
// throw new Error("useAuth must be used within an AuthProvider");
// }
// return context;
// };
// /**
// * Hook to check if the current user is a hospital admin
// * @param hospitalId Hospital ID to check against
// * @returns Boolean indicating if user is admin for that hospital
// */
// export const useIsHospitalAdmin = (hospitalId?: number | string): boolean => {
// const { responsibilities } = useAuth();
// // If no hospitalId provided, return false
// if (hospitalId === undefined) {
// return false;
// }
// // Check if user is admin for the specified hospital
// return responsibilities?.hospitalAdminFor === hospitalId;
// };
// /**
// * Hook to check if the current user is a secretary for a specific doctor
// * @param doctorId Doctor ID to check against
// * @returns Boolean indicating if user is a secretary for that doctor
// */
// export const useIsDoctorSecretary = (doctorId?: number): boolean => {
// const { responsibilities } = useAuth();
// // If no doctorId provided, return false
// if (doctorId === undefined) {
// return false;
// }
// // Check if user is a secretary for the specified doctor
// return responsibilities?.secretaryFor?.includes(doctorId) || false;
// };

View file

@ -1,43 +0,0 @@
import React, { createContext, useContext, useEffect, useState, ReactNode } from "react";
import { AuthContext } from "./auth-context";
import { ROLE_NAMES } from "common-ui";
// import { getRoles, saveRoles, deleteRoles } from "../../hooks/useJWT";
// import { ROLE_NAMES } from "../../lib/constants";
// interface RolesContextType {
// roles: string[] | null;
// setRoles: (roles: string[] | null) => void;
// refreshRoles: () => Promise<void>;
// }
// const RolesContext = createContext<RolesContextType | undefined>(undefined);
// export const RolesProvider = ({ children }: { children: ReactNode }) => {
// const [roles, setRoles] = useState<string[] | null>(null);
// const refreshRoles = async () => {
// const r = await getRoles();
// setRoles(r);
// };
// useEffect(() => {
// refreshRoles();
// }, []);
// return (
// <RolesContext.Provider value={{ roles, setRoles, refreshRoles }}>
// {children}
// </RolesContext.Provider>
// );
// };
export const useRoles = () => {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("useRoles must be used within a RolesProvider");
return ctx.roles;
};
export const useIsAdmin = () => {
const roles = useRoles();
return roles?.includes(ROLE_NAMES.ADMIN);
}

View file

@ -1,115 +0,0 @@
import React from 'react';
import { View, TouchableOpacity, Animated, Easing } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { MyText , tw , colors } from "common-ui";
import { Ionicons } from '@expo/vector-icons';
import { useThemeColor } from '@/hooks/useThemeColor';
import { IconButton } from 'react-native-paper';
interface DashboardHeaderProps {
onMenuPress: () => void;
onNotificationsPress: () => void;
onProfilePress: () => void;
onRefreshPress?: () => void;
refreshing?: boolean;
}
const DashboardHeader: React.FC<DashboardHeaderProps> = ({
onMenuPress,
onNotificationsPress,
onProfilePress,
onRefreshPress,
refreshing = false
}) => {
const accentColor = useThemeColor({ light: '#4f46e5', dark: '#818cf8' }, 'tint');
// For the refresh animation
const spinAnim = React.useRef(new Animated.Value(0)).current;
// Update animation when refreshing prop changes
React.useEffect(() => {
let animation: Animated.CompositeAnimation | null = null;
if (refreshing) {
animation = Animated.loop(
Animated.timing(spinAnim, {
toValue: 1,
duration: 1000,
easing: Easing.linear,
useNativeDriver: true,
})
);
animation.start();
} else {
spinAnim.stopAnimation();
spinAnim.setValue(0);
}
return () => {
if (animation) {
animation.stop();
}
};
}, [refreshing]);
const spin = spinAnim.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
return (
<SafeAreaView edges={['top']} style={tw`mb-2 bg-white dark:bg-gray-900`}>
<View style={tw`flex-row justify-between items-center px-4 py-2`}>
{/* Menu Button */}
<TouchableOpacity
style={tw`w-10 h-10 rounded-full bg-white dark:bg-gray-800 items-center justify-center shadow-sm`}
onPress={onMenuPress}
>
<Ionicons name="menu" size={24} color={accentColor} />
</TouchableOpacity>
{/* Logo/App Name */}
<MyText style={tw`text-xl font-bold text-gray-800 dark:text-white`}>
HealthPetal
</MyText>
{/* Right Actions */}
<View style={tw`flex-row`}>
{/* Refresh Button */}
{onRefreshPress && (
<Animated.View style={{ transform: [{ rotate: spin }], marginRight: 8 }}>
<IconButton
icon="refresh"
size={20}
onPress={onRefreshPress}
accessibilityLabel="Refresh"
disabled={refreshing}
iconColor={colors.blue1}
/>
</Animated.View>
)}
{/* Notifications */}
<TouchableOpacity
style={tw`w-10 h-10 rounded-full bg-white dark:bg-gray-800 items-center justify-center shadow-sm mr-2 relative`}
onPress={onNotificationsPress}
>
<Ionicons name="notifications" size={20} color="#6b7280" />
{/* Notification Badge */}
<View style={tw`absolute top-1 right-1 w-3 h-3 bg-red-500 rounded-full`}></View>
</TouchableOpacity>
{/* Profile */}
<TouchableOpacity
style={tw`w-10 h-10 rounded-full bg-white dark:bg-gray-800 items-center justify-center shadow-sm`}
onPress={onProfilePress}
>
<Ionicons name="person" size={20} color="#6b7280" />
</TouchableOpacity>
</View>
</View>
</SafeAreaView>
);
};
export default DashboardHeader;

View file

@ -1,278 +0,0 @@
// import { useTheme } from "@/hooks/theme-context";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import DateTimePicker, {
AndroidNativeProps,
DateTimePickerAndroid,
DateTimePickerEvent,
} from "@react-native-community/datetimepicker";
import React, { useState } from "react";
import {
Modal,
Platform,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import { useTheme, MyText } from "common-ui";
interface Props {
value: Date | null;
setValue: (date: Date | null) => void;
showLabels?: boolean; // Optional prop to control label visibility
timeOnly?: boolean; // Optional prop to show only time picker
}
type Mode = "date" | "time" | "datetime";
function DateTimePickerMod(props: Props) {
const { value, setValue, showLabels = true, timeOnly = false } = props;
const [show, setShow] = useState<boolean>(false);
const [mode, setMode] = useState<Mode>("date");
const onChange = (event: DateTimePickerEvent, selectedDate?: Date) => {
const currentDate = selectedDate || value;
if (Platform.OS === "ios") setShow(false);
setValue(currentDate);
};
const showMode = (currentMode: Mode) => {
if (Platform.OS === "android") {
DateTimePickerAndroid.open({
value: value || new Date(),
onChange: onChange,
mode: currentMode,
is24Hour: true,
display: "default",
} as AndroidNativeProps);
} else {
setShow(true);
setMode(currentMode);
}
};
const showDatepicker = () => {
showMode("date");
};
const showTimepicker = () => {
showMode("time");
};
const { theme } = useTheme();
return (
<View style={styles.container}>
{timeOnly ? (
<TouchableOpacity onPress={showTimepicker}>
{showLabels && <MyText>Select Time</MyText>}
<View
style={{
borderColor: theme.colors.gray1,
borderWidth: 0.5,
borderRadius: 4,
paddingVertical: 4,
paddingHorizontal: 4,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
gap: 4,
}}
>
<Text style={[styles.timeText, { opacity: value ? 1 : 0.5 }]}>
{value?.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
}) || "Select Time"}
</Text>
<MaterialCommunityIcons
name="clock"
size={24}
color={theme.colors.gray1}
/>
</View>
</TouchableOpacity>
) : (
<View
style={{ width: "100%", flexDirection: "row", alignItems: "stretch" }}
>
<TouchableOpacity
onPress={showDatepicker}
style={{
width: "50%",
}}
>
{showLabels && <MyText>Select Date</MyText>}
<View
style={{
flexDirection: "row",
alignItems: "center",
borderColor: theme.colors.gray1,
borderWidth: 0.5,
borderRadius: 4,
paddingVertical: 4,
paddingHorizontal: 4,
justifyContent: "space-between",
}}
>
<Text style={[styles.dateText, { opacity: value ? 1 : 0.5 }]}>{value?.toLocaleDateString() || "Select Date"}</Text>
<MaterialCommunityIcons
name="calendar"
size={24}
color={theme.colors.gray1}
/>
</View>
</TouchableOpacity>
<View style={styles.spacerHorizontalSmall} />
<TouchableOpacity
onPress={showTimepicker}
style={{
width: "50%",
}}
>
{showLabels && <MyText>Select Time</MyText>}
<View
style={{
borderColor: theme.colors.gray1,
borderWidth: 0.5,
borderRadius: 4,
paddingVertical: 4,
paddingHorizontal: 4,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
gap: 4,
}}
>
<Text style={[styles.timeText, { opacity: value ? 1 : 0.5 }]}>
{value?.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
}) || "Select Time"}
</Text>
<MaterialCommunityIcons
name="clock"
size={24}
color={theme.colors.gray1}
/>
</View>
</TouchableOpacity>
</View>
)}
{/* Conditional rendering for iOS, as it uses the declarative API */}
{show && Platform.OS === "ios" && (
<Modal
transparent
animationType="fade"
visible={show}
onRequestClose={() => setShow(false)}
>
<View style={styles.modalOverlay}>
<View style={styles.pickerContainer}>
<DateTimePicker
testID="dateTimePicker"
value={value || new Date()}
mode={mode}
is24Hour={true}
display="default"
onChange={onChange}
/>
<TouchableOpacity
onPress={() => setShow(false)}
style={styles.doneButton}
>
<Text style={styles.doneButtonText}>Done</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
)}
</View>
);
}
export default DateTimePickerMod;
const styles = StyleSheet.create({
container: {
// flex: 1, // Remove flex to avoid taking up extra space
justifyContent: "center",
alignItems: "flex-start",
padding: 0, // Reduce padding for compactness
marginBottom: 16, // Add margin for spacing in forms
},
iconRow: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
marginBottom: 20,
},
iconRowSingleLine: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
marginBottom: 0,
},
iconNoBg: {
backgroundColor: "transparent",
borderRadius: 0,
padding: 0,
elevation: 0,
},
spacerHorizontal: {
width: 30,
},
spacerHorizontalSmall: {
width: 8,
},
spacer: {
height: 20, // Add some space between buttons
},
selectedText: {
marginTop: 30,
fontSize: 18,
fontWeight: "bold",
textAlign: "center",
},
timeTextContainer: {
justifyContent: "center",
alignItems: "center",
},
timeText: {
fontSize: 15,
fontWeight: "500",
color: "#333",
marginLeft: 2,
},
dateText: {
fontSize: 15,
fontWeight: "500",
color: "#333",
marginLeft: 2,
marginRight: 2,
},
modalOverlay: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
pickerContainer: {
width: "80%",
backgroundColor: "white",
borderRadius: 10,
padding: 20,
elevation: 5,
},
doneButton: {
marginTop: 10,
backgroundColor: "#007bff",
borderRadius: 5,
padding: 10,
alignItems: "center",
},
doneButtonText: {
color: "white",
fontWeight: "bold",
},
});

View file

@ -1,77 +0,0 @@
import React from 'react';
import { View } from 'react-native';
import { MyText , tw } from "common-ui";
import { Ionicons } from '@expo/vector-icons';
// Define the types
export interface DoctorWiseCount {
doctorName: string;
doctorId: number;
fee: number;
issuedTokens: number;
totalAmount: number;
}
export interface DayAccountData {
doctorWiseCount: DoctorWiseCount[];
date: string;
totalAmount: number;
settled: boolean;
}
interface DayAccountViewProps {
dayData: DayAccountData;
}
const DayAccountView: React.FC<DayAccountViewProps> = ({ dayData }) => {
return (
<View
style={tw`mb-4 p-4 bg-white dark:bg-gray-800 rounded-xl shadow border border-gray-200 dark:border-gray-700`}
>
<View style={tw`flex-row justify-between items-center mb-3`}>
<MyText style={tw`text-lg font-bold text-gray-800 dark:text-white`}>
{dayData.date}
</MyText>
<View style={tw`flex-row items-center`}>
<MyText style={tw`text-lg font-bold text-gray-800 dark:text-white mr-2`}>
{dayData.totalAmount}
</MyText>
{dayData.settled ? (
<View style={tw`flex-row items-center bg-green-100 dark:bg-green-900/30 px-2 py-1 rounded-full`}>
<Ionicons name="checkmark-circle" size={16} color="#16a34a" />
<MyText style={tw`text-green-700 dark:text-green-400 ml-1 text-xs`}>Settled</MyText>
</View>
) : (
<View style={tw`flex-row items-center bg-yellow-100 dark:bg-yellow-900/30 px-2 py-1 rounded-full`}>
<Ionicons name="alert-circle" size={16} color="#f59e0b" />
<MyText style={tw`text-yellow-700 dark:text-yellow-400 ml-1 text-xs`}>Pending</MyText>
</View>
)}
</View>
</View>
<View style={tw`mt-3`}>
{dayData.doctorWiseCount.map((doctor, idx) => (
<View
key={idx}
style={tw`flex-row justify-between items-center p-3 bg-gray-50 dark:bg-gray-700/30 rounded-lg mb-2`}
>
<View>
<MyText style={tw`font-medium text-gray-800 dark:text-white`}>
{doctor.doctorName}
</MyText>
<MyText style={tw`text-xs text-gray-600 dark:text-gray-400`}>
{doctor.fee} × {doctor.issuedTokens} tokens
</MyText>
</View>
<MyText style={tw`font-bold text-gray-800 dark:text-white`}>
{doctor.totalAmount}
</MyText>
</View>
))}
</View>
</View>
);
};
export default DayAccountView;

View file

@ -1,32 +0,0 @@
import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols';
import { StyleProp, ViewStyle } from 'react-native';
export function IconSymbol({
name,
size = 24,
color,
style,
weight = 'regular',
}: {
name: SymbolViewProps['name'];
size?: number;
color: string;
style?: StyleProp<ViewStyle>;
weight?: SymbolWeight;
}) {
return (
<SymbolView
weight={weight}
tintColor={color}
resizeMode="scaleAspectFit"
name={name}
style={[
{
width: size,
height: size,
},
style,
]}
/>
);
}

View file

@ -1,41 +0,0 @@
// Fallback for using MaterialIcons on Android and web.
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { SymbolWeight, SymbolViewProps } from 'expo-symbols';
import { ComponentProps } from 'react';
import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native';
type IconMapping = Record<SymbolViewProps['name'], ComponentProps<typeof MaterialIcons>['name']>;
type IconSymbolName = keyof typeof MAPPING;
/**
* Add your SF Symbols to Material Icons mappings here.
* - see Material Icons in the [Icons Directory](https://icons.expo.fyi).
* - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app.
*/
const MAPPING = {
'house.fill': 'home',
'paperplane.fill': 'send',
'chevron.left.forwardslash.chevron.right': 'code',
'chevron.right': 'chevron-right',
} as IconMapping;
/**
* An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web.
* This ensures a consistent look across platforms, and optimal resource usage.
* Icon `name`s are based on SF Symbols and require manual mapping to Material Icons.
*/
export function IconSymbol({
name,
size = 24,
color,
style,
}: {
name: IconSymbolName;
size?: number;
color: string | OpaqueColorValue;
style?: StyleProp<TextStyle>;
weight?: SymbolWeight;
}) {
return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />;
}

View file

@ -1,19 +0,0 @@
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { BlurView } from 'expo-blur';
import { StyleSheet } from 'react-native';
export default function BlurTabBarBackground() {
return (
<BlurView
// System chrome material automatically adapts to the system's theme
// and matches the native tab bar appearance on iOS.
tint="systemChromeMaterial"
intensity={100}
style={StyleSheet.absoluteFill}
/>
);
}
export function useBottomTabOverflow() {
return useBottomTabBarHeight();
}

View file

@ -1,6 +0,0 @@
// This is a shim for web and Android where the tab bar is generally opaque.
export default undefined;
export function useBottomTabOverflow() {
return 0;
}

View file

@ -1,26 +0,0 @@
/**
* Below are the colors that are used in the app. The colors are defined in the light and dark mode.
* There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
*/
const tintColorLight = '#0a7ea4';
const tintColorDark = '#fff';
export const Colors = {
light: {
text: '#11181C',
background: '#fff',
tint: tintColorLight,
icon: '#687076',
tabIconDefault: '#687076',
tabIconSelected: tintColorLight,
},
dark: {
text: '#ECEDEE',
background: '#151718',
tint: tintColorDark,
icon: '#9BA1A6',
tabIconDefault: '#9BA1A6',
tabIconSelected: tintColorDark,
},
};

View file

@ -1,12 +0,0 @@
/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
rootDir: '..',
testMatch: ['<rootDir>/e2e/**/*.test.js'],
testTimeout: 120000,
maxWorkers: 1,
globalSetup: 'detox/runners/jest/globalSetup',
globalTeardown: 'detox/runners/jest/globalTeardown',
reporters: ['detox/runners/jest/reporter'],
testEnvironment: 'detox/runners/jest/testEnvironment',
verbose: true,
};

View file

@ -1,23 +0,0 @@
describe('Example', () => {
beforeAll(async () => {
await device.launchApp();
});
beforeEach(async () => {
await device.reloadReactNative();
});
it('should have welcome screen', async () => {
await expect(element(by.id('welcome'))).toBeVisible();
});
it('should show hello screen after tap', async () => {
await element(by.id('hello_button')).tap();
await expect(element(by.text('Hello!!!'))).toBeVisible();
});
it('should show world screen after tap', async () => {
await element(by.id('world_button')).tap();
await expect(element(by.text('World!!!'))).toBeVisible();
});
});

View file

@ -1 +0,0 @@
export { useColorScheme } from 'react-native';

View file

@ -1,21 +0,0 @@
import { useEffect, useState } from 'react';
import { useColorScheme as useRNColorScheme } from 'react-native';
/**
* To support static rendering, this value needs to be re-calculated on the client side for web
*/
export function useColorScheme() {
const [hasHydrated, setHasHydrated] = useState(false);
useEffect(() => {
setHasHydrated(true);
}, []);
const colorScheme = useRNColorScheme();
if (hasHydrated) {
return colorScheme;
}
return 'light';
}

View file

@ -1,13 +0,0 @@
import React, { useEffect, useState } from 'react';
import { getCurrentUserId } from '@/utils/getCurrentUserId';
export function useCurrentUserId(): {userId:number|null, refetchUserId: () => void} {
const [userId, setUserId] = useState<number | null>(null);
const refetchUserId = React.useCallback(() => {
getCurrentUserId().then(setUserId);
},[])
useEffect(() => {
getCurrentUserId().then(setUserId);
}, []);
return {userId,refetchUserId};
}

View file

@ -1,26 +0,0 @@
import { useFocusEffect, useNavigation } from "expo-router";
import React from "react";
function useHideDrawerHeader() {
const navigation = useNavigation();
useFocusEffect(() => {
let drawerNav = navigation.getParent();
const drawerNavList:any = [];
// Collect all parent navigators
while (drawerNav) {
drawerNavList.push(drawerNav);
drawerNav = drawerNav.getParent();
}
drawerNavList.at(-3)?.setOptions({ headerShown: false });
return () => {
drawerNavList.at(-3)?.setOptions({ headerShown: true });
};
});
return null;
}
export default useHideDrawerHeader;

View file

@ -1,49 +1,15 @@
// import { StorageService } from '@/lib/StorageService'; import { StorageService } from 'common-ui'
import {StorageService} from 'common-ui';
export const JWT_KEY = 'jwt_token'; export const JWT_KEY = 'jwt_token'
export const ROLES_KEY = 'user_roles';
export const USER_ID_KEY = 'userId';
export async function saveUserId(userId:string) {
await StorageService.setItem(USER_ID_KEY, userId);
}
export async function getUserId() {
return await StorageService.getItem(USER_ID_KEY);
}
export async function saveJWT(token: string) { export async function saveJWT(token: string) {
await StorageService.setItem(JWT_KEY, token); await StorageService.setItem(JWT_KEY, token)
} }
export async function getJWT() { export async function getJWT() {
return await StorageService.getItem(JWT_KEY); return await StorageService.getItem(JWT_KEY)
} }
export async function deleteJWT() { export async function deleteJWT() {
await StorageService.removeItem(JWT_KEY); await StorageService.removeItem(JWT_KEY)
}
export async function saveRoles(roles: string[]) {
await StorageService.setItem(ROLES_KEY, JSON.stringify(roles));
}
export async function getRoles(): Promise<string[] | null> {
const jwt = await getJWT();
if (!jwt) {
StorageService.removeItem(ROLES_KEY);
return null;
}
const rolesStr = await StorageService.getItem(ROLES_KEY);
if (!rolesStr) return null;
try {
return JSON.parse(rolesStr);
} catch {
return null;
}
}
export async function deleteRoles() {
await StorageService.removeItem(ROLES_KEY);
} }

View file

@ -1,31 +0,0 @@
import { usePhonepeCreds } from '@/api-hooks/payment.api';
import { useEffect } from 'react';
import PhonePePaymentSDK from 'react-native-phonepe-pg';
export function usePhonepeSdk() {
const { data: creds, isLoading, isError } = usePhonepeCreds();
useEffect(() => {
if (creds && creds.clientId && creds.clientVersion) {
PhonePePaymentSDK.init('SANDBOX', creds.clientId, creds.clientId, true);
}
}, [creds]);
const startTransaction = async (orderId: string, token: string) => {
try {
const request = {
orderId,
token,
merchantId: creds?.merchantId,
paymentMode: { type: 'PAY_PAGE' }
};
const stringReq = JSON.stringify(request);
const response = await PhonePePaymentSDK.startTransaction(stringReq, null);
return response;
} catch (error) {
throw error;
}
};
return { startTransaction, isLoading, isError, creds };
}

View file

@ -1,21 +0,0 @@
/**
* Learn more about light and dark modes:
* https://docs.expo.dev/guides/color-schemes/
*/
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
export function useThemeColor(
props: { light?: string; dark?: string },
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
) {
const theme = useColorScheme() ?? 'light';
const colorFromProps = props[theme];
if (colorFromProps) {
return colorFromProps;
} else {
return Colors[theme][colorName];
}
}

View file

@ -1,112 +0,0 @@
#!/usr/bin/env node
/**
* This script is used to reset the project to a blank state.
* It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file.
* You can remove the `reset-project` script from package.json and safely delete this file after running it.
*/
const fs = require("fs");
const path = require("path");
const readline = require("readline");
const root = process.cwd();
const oldDirs = ["app", "components", "hooks", "constants", "scripts"];
const exampleDir = "app-example";
const newAppDir = "app";
const exampleDirPath = path.join(root, exampleDir);
const indexContent = `import { Text, View } from "react-native";
export default function Index() {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>Edit app/index.tsx to edit this screen.</Text>
</View>
);
}
`;
const layoutContent = `import { Stack } from "expo-router";
export default function RootLayout() {
return <Stack />;
}
`;
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const moveDirectories = async (userInput) => {
try {
if (userInput === "y") {
// Create the app-example directory
await fs.promises.mkdir(exampleDirPath, { recursive: true });
console.log(`📁 /${exampleDir} directory created.`);
}
// Move old directories to new app-example directory or delete them
for (const dir of oldDirs) {
const oldDirPath = path.join(root, dir);
if (fs.existsSync(oldDirPath)) {
if (userInput === "y") {
const newDirPath = path.join(root, exampleDir, dir);
await fs.promises.rename(oldDirPath, newDirPath);
console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`);
} else {
await fs.promises.rm(oldDirPath, { recursive: true, force: true });
console.log(`❌ /${dir} deleted.`);
}
} else {
console.log(`➡️ /${dir} does not exist, skipping.`);
}
}
// Create new /app directory
const newAppDirPath = path.join(root, newAppDir);
await fs.promises.mkdir(newAppDirPath, { recursive: true });
console.log("\n📁 New /app directory created.");
// Create index.tsx
const indexPath = path.join(newAppDirPath, "index.tsx");
await fs.promises.writeFile(indexPath, indexContent);
console.log("📄 app/index.tsx created.");
// Create _layout.tsx
const layoutPath = path.join(newAppDirPath, "_layout.tsx");
await fs.promises.writeFile(layoutPath, layoutContent);
console.log("📄 app/_layout.tsx created.");
console.log("\n✅ Project reset complete. Next steps:");
console.log(
`1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${
userInput === "y"
? `\n3. Delete the /${exampleDir} directory when you're done referencing it.`
: ""
}`
);
} catch (error) {
console.error(`❌ Error during script execution: ${error.message}`);
}
};
rl.question(
"Do you want to move existing files to /app-example instead of deleting them? (Y/n): ",
(answer) => {
const userInput = answer.trim().toLowerCase() || "y";
if (userInput === "y" || userInput === "n") {
moveDirectories(userInput).finally(() => rl.close());
} else {
console.log("❌ Invalid input. Please enter 'Y' or 'N'.");
rl.close();
}
}
);

View file

@ -1,59 +0,0 @@
import axiosParent from 'axios';
import { FORCE_LOGOUT_EVENT } from 'common-ui/src/lib/const-strs';
import { DeviceEventEmitter } from 'react-native'
import { getJWT } from '@/hooks/useJWT';
import { BASE_API_URL } from 'common-ui';
// export const API_BASE_URL = 'http://192.168.100.95:4000'; // Change to your API base URL
// const API_BASE_URL = 'https://www.technocracy.ovh/mf'; // Change to your API base URL
// const API_BASE_URL = 'http://10.195.26.42:4000'; // Change to your API base URL
// const API_BASE_URL = 'http://localhost:4000/api/mobile/'; // Change to your API base URL
// const API_BASE_URL = 'https://car-safar.com/api/mobile/'; // Change to your API base URL
const axios = axiosParent.create({
baseURL: BASE_API_URL + '/api/v1',
timeout: 60000,
// headers: {
// 'Content-Type': 'application/json',
// },
});
axios.interceptors.request.use(
async (config) => {
const token = await getJWT();
if (token) {
config.headers = config.headers || {};
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
axios.interceptors.response.use(
(response) => response,
(error) => {
const status = error?.status;
const msg = error.response?.data?.error;
if (status === 401 && msg.startsWith('Access denied')) {
// Handle unauthorized access
DeviceEventEmitter.emit(FORCE_LOGOUT_EVENT);
}
const message = error?.response?.data?.error;
if (msg) {
// Optionally, you can attach the message to the error object or throw a new error
const err = new Error(msg);
// Optionally attach the original error for debugging
(err as any).original = error;
return Promise.reject(err);
}
return Promise.reject(error);
}
);
export default axios;

View file

@ -1,71 +0,0 @@
import {useAddPushToken, useHasPushToken } from "@/api-hooks/user.api";
import React from "react";
import { useNotification } from "./notif-context";
import { BottomDialog } from "common-ui";
import { MyText } from "common-ui";
import { View, Linking } from "react-native";
import { tw } from "common-ui";
import { MyButton } from "common-ui";
import { useAuth } from "@/components/context/auth-context";
interface Props {}
function NotifChecker(props: Props) {
const {} = props;
const [showPermissionDialog, setShowPermissionDialog] = React.useState(false);
const {isLoggedIn} = useAuth();
const { data: hasPushToken, isLoading, isError } = useHasPushToken({enabled: isLoggedIn});
const { mutate: addPushToken } = useAddPushToken();
const { notifPermission, expoPushToken } = useNotification();
React.useEffect(() => {
if(isLoggedIn && !hasPushToken && notifPermission =='granted') {
addPushToken(expoPushToken!);
}
},[isLoggedIn, hasPushToken])
React.useEffect(() => {
if (notifPermission === "denied") {
setShowPermissionDialog(true);
}
}, [notifPermission]);
return (
<>
<BottomDialog
open={showPermissionDialog}
onClose={() => setShowPermissionDialog(false)}
>
<View style={tw`flex flex-col h-64 p-4`}>
<MyText weight="semibold" color="red1" style={tw`mb-2 text-lg`}>
Notification Permission Denied
</MyText>
<MyText>
It seems you have denied notification permissions. Please enable
them in your device settings.
</MyText>
<View style={tw`flex flex-row gap-3 mt-auto justify-center`}>
<MyButton
fillColor="red1"
onPress={() => setShowPermissionDialog(false)}
style={tw`flex-1`}
>
Cancel
</MyButton>
<MyButton
fillColor="blue1"
onPress={() => {
Linking.openSettings();
}}
style={tw`flex-1`}
>
Settings
</MyButton>
</View>
</View>
</BottomDialog>
</>
);
}
export default NotifChecker;

View file

@ -1,111 +0,0 @@
import React, {
createContext,
useContext,
useState,
useEffect,
useRef,
ReactNode,
} from "react";
import * as Notifications from "expo-notifications";
import { registerForPushNotificationsAsync } from "./notif-register";
import { useRouter } from "expo-router";
import { NotificationToast } from "../toaster";
import { NOTIF_PERMISSION_DENIED } from "common-ui/src/lib/const-strs";
interface NotificationContextType {
expoPushToken: string | null;
notification: Notifications.Notification | null;
error: Error | null;
notifPermission: 'pending' | 'granted' | 'denied'
}
export const NotificationContext = createContext<
NotificationContextType | undefined
>(undefined);
export const useNotification = () => {
const context = useContext(NotificationContext);
if (context === undefined) {
throw new Error(
"useNotification must be used within a NotificationProvider"
);
}
return context;
};
interface NotificationProviderProps {
children: ReactNode;
}
export const NotificationProvider: React.FC<NotificationProviderProps> = ({
children,
}) => {
const [expoPushToken, setExpoPushToken] = useState<string | null>(null);
const [notification, setNotification] =
useState<Notifications.Notification | null>(null);
const [error, setError] = useState<Error | null>(null);
const [notifPermission, setNotifPermission] = React.useState<NotificationContextType["notifPermission"]>("pending");
const notificationListener = useRef<any>(null);
const responseListener = useRef<any>(null);
const router = useRouter();
useEffect(() => {
registerForPushNotificationsAsync()
.then((token) => {
setExpoPushToken(token);
setNotifPermission("granted");
})
.catch((errorRaw) => {
const err = String(errorRaw).slice(7); //remove the "Error: " string component in beginning
if (err === NOTIF_PERMISSION_DENIED) {
setNotifPermission("denied");
}
});
notificationListener.current =
Notifications.addNotificationReceivedListener((notification) => {
setNotification(notification);
// Show a visible toast when app is in foreground
const content = notification.request?.content;
if (content) {
NotificationToast(
content.title || "Notification",
content.body || "",
content.data || {}
);
}
});
responseListener.current =
Notifications.addNotificationResponseReceivedListener((response) => {
const data = response.notification.request.content.data;
if (data && data.doctorId) {
router.navigate(`/(drawer)/dashboard`);
} else if (data && data.tokenId) {
router.navigate(`/(drawer)/dashboard`);
}
});
return () => {
if (notificationListener.current) {
Notifications.removeNotificationSubscription(
notificationListener.current
);
}
if (responseListener.current) {
Notifications.removeNotificationSubscription(responseListener.current);
}
};
}, []);
return (
<NotificationContext.Provider
value={{ expoPushToken, notification, error, notifPermission }}
>
{children}
</NotificationContext.Provider>
);
};

View file

@ -1,51 +0,0 @@
import * as Notifications from "expo-notifications";
import * as Device from "expo-device";
import Constants from "expo-constants";
import { Platform } from "react-native";
import { NOTIF_PERMISSION_DENIED } from "common-ui/src/lib/const-strs";
export async function registerForPushNotificationsAsync() {
if (Platform.OS === "android") {
await Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
});
}
if (Device.isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
throw new Error(
NOTIF_PERMISSION_DENIED
);
}
const projectId =
Constants?.expoConfig?.extra?.eas?.projectId ??
Constants?.easConfig?.projectId;
if (!projectId) {
throw new Error("Project ID not found");
}
try {
const pushTokenString = (
await Notifications.getExpoPushTokenAsync({
projectId,
})
).data;
console.log(pushTokenString);
return pushTokenString;
} catch (e: unknown) {
throw new Error(`${e}`);
}
} else {
throw new Error("Must use physical device for push notifications");
}
}

View file

@ -1,26 +0,0 @@
// Types
export interface Banner {
id: number;
name: string;
imageUrl: string;
description?: string;
skuIds?: number[];
redirectUrl?: string;
serialNum: number;
isActive: boolean;
createdAt: string;
lastUpdated: string;
}
export interface CreateBannerPayload {
name: string;
imageUrl: string;
description?: string;
skuIds?: number[];
redirectUrl?: string;
serialNum: number;
}
export interface UpdateBannerPayload extends Partial<CreateBannerPayload> {
isActive?: boolean;
}

View file

@ -150,7 +150,6 @@ export default function CouponForm({ onSubmit, isLoading, initialValues }: Coupo
const newSelection = current.includes(userId) const newSelection = current.includes(userId)
? current.filter(id => id !== userId) ? current.filter(id => id !== userId)
: [...current, userId]; : [...current, userId];
console.log('Toggling user:', userId, 'New selection:', newSelection);
setFieldValue('applicableUsers', newSelection); setFieldValue('applicableUsers', newSelection);
}; };

View file

@ -1,14 +0,0 @@
import {jwtDecode} from 'jwt-decode';
import { getJWT } from '@/hooks/useJWT';
export async function getCurrentUserId(): Promise<number | null> {
const token = await getJWT();
if (!token) return null;
try {
const decoded: any = jwtDecode(token);
// Adjust this if your JWT uses a different field for user id
return decoded.id || decoded.userId || null;
} catch {
return null;
}
}

View file

@ -1,9 +0,0 @@
import { Stack } from 'expo-router'
function AboutLayout() {
return (
<Stack screenOptions={{ headerShown: true, title: "About" }} />
)
}
export default AboutLayout

View file

@ -1,167 +0,0 @@
import React from 'react';
import { View, ScrollView, Linking } from 'react-native';
import { Image } from 'expo-image';
import { AppContainer, MyText, tw, MyTouchableOpacity } from 'common-ui';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import FontAwesome5 from '@expo/vector-icons/FontAwesome5';
export default function About() {
const openLink = (url: string) => {
Linking.openURL(url).catch((err) => {});
};
return (
<AppContainer>
<ScrollView
style={tw`flex-1 bg-gray-50`}
contentContainerStyle={tw`pb-12`}
showsVerticalScrollIndicator={false}
>
{/* Hero Section */}
<View style={tw`bg-white pb-8 rounded-b-3xl shadow-sm mb-6 overflow-hidden`}>
<View style={tw`bg-green-600 h-32 absolute top-0 left-0 right-0`} />
<View style={tw`px-6 pt-16 items-center`}>
<View style={tw`w-24 h-24 bg-white rounded-2xl shadow-lg items-center justify-center mb-4`}>
<FontAwesome5 name="store" size={40} color="#16A34A" />
</View>
<MyText style={tw`text-3xl font-bold text-gray-900 text-center mb-2`}>
Meat Farmer
</MyText>
<MyText style={tw`text-base text-gray-600 text-center px-4 leading-6`}>
Bringing local trust and online convenience together.
</MyText>
</View>
</View>
{/* Mission Cards */}
<View style={tw`px-4 mb-6`}>
<MyText style={tw`text-lg font-bold text-gray-900 mb-4 ml-2`}>Our Mission</MyText>
<View style={tw`flex-row flex-wrap justify-between`}>
<View style={tw`w-[48%] bg-white p-4 rounded-2xl shadow-sm mb-4 border border-gray-100`}>
<View style={tw`w-10 h-10 bg-blue-50 rounded-full items-center justify-center mb-3`}>
<MaterialIcons name="location-on" size={20} color="#3B82F6" />
</View>
<MyText style={tw`font-bold text-gray-900 mb-1`}>Local Roots</MyText>
<MyText style={tw`text-xs text-gray-500 leading-4`}>Based in MBNR, supporting our community.</MyText>
</View>
<View style={tw`w-[48%] bg-white p-4 rounded-2xl shadow-sm mb-4 border border-gray-100`}>
<View style={tw`w-10 h-10 bg-green-50 rounded-full items-center justify-center mb-3`}>
<MaterialIcons name="attach-money" size={20} color="#10B981" />
</View>
<MyText style={tw`font-bold text-gray-900 mb-1`}>Best Price</MyText>
<MyText style={tw`text-xs text-gray-500 leading-4`}>Minimizing costs to enhance your buying experience.</MyText>
</View>
<View style={tw`w-[48%] bg-white p-4 rounded-2xl shadow-sm mb-4 border border-gray-100`}>
<View style={tw`w-10 h-10 bg-purple-50 rounded-full items-center justify-center mb-3`}>
<MaterialIcons name="verified" size={20} color="#8B5CF6" />
</View>
<MyText style={tw`font-bold text-gray-900 mb-1`}>Quality First</MyText>
<MyText style={tw`text-xs text-gray-500 leading-4`}>Committed to fresh, high-quality meat products.</MyText>
</View>
<View style={tw`w-[48%] bg-white p-4 rounded-2xl shadow-sm mb-4 border border-gray-100`}>
<View style={tw`w-10 h-10 bg-orange-50 rounded-full items-center justify-center mb-3`}>
<MaterialIcons name="emoji-people" size={20} color="#F97316" />
</View>
<MyText style={tw`font-bold text-gray-900 mb-1`}>Farmers First</MyText>
<MyText style={tw`text-xs text-gray-500 leading-4`}>Dedicated to supporting local farmers.</MyText>
</View>
</View>
</View>
{/* Sourcing Section */}
<View style={tw`px-4 mb-6`}>
<View style={tw`bg-white p-5 rounded-2xl shadow-sm border border-gray-100`}>
<View style={tw`flex-row items-center mb-4`}>
<View style={tw`w-10 h-10 bg-teal-50 rounded-full items-center justify-center mr-3`}>
<FontAwesome5 name="seedling" size={18} color="#14B8A6" />
</View>
<MyText style={tw`text-lg font-bold text-gray-900`}>Sourcing & Quality</MyText>
</View>
<View style={tw`space-y-4`}>
<View style={tw`flex-row items-start`}>
<MaterialIcons name="check-circle" size={20} color="#14B8A6" style={tw`mt-0.5 mr-2`} />
<MyText style={tw`text-gray-600 flex-1 text-sm`}>
All items are procured directly from authorized dealers.
</MyText>
</View>
<View style={tw`flex-row items-start`}>
<MaterialIcons name="check-circle" size={20} color="#14B8A6" style={tw`mt-0.5 mr-2`} />
<MyText style={tw`text-gray-600 flex-1 text-sm`}>
100% local products sourced from trusted suppliers.
</MyText>
</View>
<View style={tw`flex-row items-start`}>
<MaterialIcons name="check-circle" size={20} color="#14B8A6" style={tw`mt-0.5 mr-2`} />
<MyText style={tw`text-gray-600 flex-1 text-sm`}>
All products are purely <MyText style={tw`font-bold text-teal-700`}>Halal</MyText> certified.
</MyText>
</View>
</View>
</View>
</View>
{/* Payments & Refunds Section */}
<View style={tw`px-4 mb-8`}>
<View style={tw`bg-white p-5 rounded-2xl shadow-sm border border-gray-100`}>
<View style={tw`flex-row items-center mb-4`}>
<View style={tw`w-10 h-10 bg-indigo-50 rounded-full items-center justify-center mr-3`}>
<MaterialIcons name="payment" size={20} color="#6366F1" />
</View>
<MyText style={tw`text-lg font-bold text-gray-900`}>Payments & Refunds</MyText>
</View>
<View style={tw`bg-gray-50 p-4 rounded-xl mb-4`}>
<MyText style={tw`text-sm text-gray-700 leading-5 mb-2`}>
<MyText style={tw`font-bold`}>Payment Options:</MyText> Online or Cash on Delivery (COD).
</MyText>
<MyText style={tw`text-sm text-gray-700 leading-5`}>
<MyText style={tw`font-bold`}>Complaints:</MyText> Must be raised within 12 hours of delivery.
</MyText>
</View>
<View style={tw`space-y-3`}>
<View style={tw`flex-row items-start`}>
<View style={tw`w-1.5 h-1.5 rounded-full bg-indigo-400 mt-2 mr-2`} />
<MyText style={tw`text-gray-600 flex-1 text-sm`}>
Refunds are processed to the original payment method.
</MyText>
</View>
<View style={tw`flex-row items-start`}>
<View style={tw`w-1.5 h-1.5 rounded-full bg-indigo-400 mt-2 mr-2`} />
<MyText style={tw`text-gray-600 flex-1 text-sm`}>
Alternatively, receive a refund coupon for future purchases.
</MyText>
</View>
<View style={tw`flex-row items-start`}>
<View style={tw`w-1.5 h-1.5 rounded-full bg-indigo-400 mt-2 mr-2`} />
<MyText style={tw`text-gray-600 flex-1 text-sm`}>
Processing time: Up to 3 business days.
</MyText>
</View>
</View>
</View>
</View>
{/* Footer */}
<View style={tw`items-center px-6`}>
<MyText style={tw`text-gray-400 text-sm mb-2`}>Follow us</MyText>
<View style={tw`flex-row space-x-6 mb-6`}>
<MyTouchableOpacity style={tw`p-2 bg-white rounded-full shadow-sm`}>
<FontAwesome5 name="instagram" size={20} color="#E1306C" />
</MyTouchableOpacity>
<MyTouchableOpacity style={tw`p-2 bg-white rounded-full shadow-sm`}>
<FontAwesome5 name="facebook" size={20} color="#1877F2" />
</MyTouchableOpacity>
<MyTouchableOpacity style={tw`p-2 bg-white rounded-full shadow-sm`}>
<FontAwesome5 name="twitter" size={20} color="#1DA1F2" />
</MyTouchableOpacity>
</View>
<MyText style={tw`text-gray-400 text-xs`}>
© 2024 Meat Farmer. All rights reserved.
</MyText>
</View>
</ScrollView>
</AppContainer>
);
}

View file

@ -561,34 +561,6 @@ export default function MyOrders() {
createRazorpayOrderMutation.mutate({ orderId: orderId.toString() }); createRazorpayOrderMutation.mutate({ orderId: orderId.toString() });
}; };
// const initiateRazorpayPayment = (razorpayOrderId: string, key: string, amount: number) => {
// const options = {
// key,
// amount: amount * 100, // in paisa
// currency: 'INR',
// order_id: razorpayOrderId,
// name: 'Meat Farmer',
// description: 'Order Payment Retry',
// prefill: {
// // Add user details if available
// },
// };
// RazorpayCheckout.open(options)
// .then((data: any) => {
// // Payment success
// verifyPaymentMutation.mutate({
// razorpay_payment_id: data.razorpay_payment_id,
// razorpay_order_id: data.razorpay_order_id,
// razorpay_signature: data.razorpay_signature,
// });
// })
// .catch((error: any) => {
// Alert.alert('Payment Failed', 'Payment failed. Please try again.');
// refetch();
// });
// };
if (isLoading && currentPage === 1) { if (isLoading && currentPage === 1) {
return ( return (
<View style={tw`flex-1 justify-center items-center bg-gray-50`}> <View style={tw`flex-1 justify-center items-center bg-gray-50`}>

View file

@ -8,7 +8,7 @@ import { Stack } from "expo-router";
import "react-native-reanimated"; import "react-native-reanimated";
import { useColorScheme } from "@/hooks/useColorScheme"; import { useColorScheme } from "@/hooks/useColorScheme";
import { Appearance, Dimensions, StatusBar, View } from "react-native"; import { Appearance, StatusBar, View } from "react-native";
import { QueryClientProvider } from "@tanstack/react-query"; import { QueryClientProvider } from "@tanstack/react-query";
import { theme , MyStatusBar } from "common-ui"; import { theme , MyStatusBar } from "common-ui";
import queryClient from "@/utils/queryClient"; import queryClient from "@/utils/queryClient";
@ -37,8 +37,6 @@ export default function RootLayout() {
SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"), SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
}); });
console.log('from layout')
React.useEffect(() => { React.useEffect(() => {
Appearance.setColorScheme('light') Appearance.setColorScheme('light')
}, []); }, []);

View file

@ -1,55 +0,0 @@
import constants from "@/src/constants";
const GOOGLE_CLIENT_ID = constants.GOOGLE_CLIENT_ID;
export async function GET(request: Request) {
if (!GOOGLE_CLIENT_ID) {
return Response.json(
{ error: "Missing GOOGLE_CLIENT_ID environment variable" },
{ status: 500 }
);
}
const url = new URL(request.url);
let idpClientId: string;
const internalClient = url.searchParams.get("client_id");
const redirectUri = url.searchParams.get("redirect_uri");
let platform;
if (redirectUri === constants.APP_SCHEME) {
platform = "mobile";
} else if (redirectUri === constants.BASE_URL) {
platform = "web";
} else {
return Response.json({ error: "Invalid redirect_uri" }, { status: 400 });
}
// use state to drive redirect back to platform
let state = platform + "|" + url.searchParams.get("state");
if (internalClient === "google") {
idpClientId = GOOGLE_CLIENT_ID;
} else {
return Response.json({ error: "Invalid client" }, { status: 400 });
}
// additional enforcement
if (!state) {
return Response.json({ error: "Invalid state" }, { status: 400 });
}
const params = new URLSearchParams({
client_id: idpClientId,
redirect_uri: constants.BASE_URL + "/api/auth/callback",
response_type: "code",
scope: url.searchParams.get("scope") || "identity",
state: state,
prompt: "select_account",
});
return Response.redirect(constants.GOOGLE_AUTH_URL + "?" + params.toString());
}

View file

@ -1,28 +0,0 @@
// import { BASE_URL, APP_SCHEME } from "@/utils/constants";
import constants from "@/src/constants";
const BASE_URL = constants.BASE_URL;
const APP_SCHEME = constants.APP_SCHEME;
export async function GET(request: Request) {
const incomingParams = new URLSearchParams(request.url.split("?")[1]);
const combinedPlatformAndState = incomingParams.get("state");
if (!combinedPlatformAndState) {
return Response.json({ error: "Invalid state" }, { status: 400 });
}
// strip platform to return state as it was set on the client
const platform = combinedPlatformAndState.split("|")[0];
const state = combinedPlatformAndState.split("|")[1];
const outgoingParams = new URLSearchParams({
code: incomingParams.get("code")?.toString() || "",
state,
});
return Response.redirect(
(platform === "web" ? BASE_URL : APP_SCHEME) +
"?" +
outgoingParams.toString()
);
}

View file

@ -1,3 +0,0 @@
export async function GET(request:Request) {
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

View file

@ -1,30 +0,0 @@
<svg width="292" height="292" viewBox="0 0 292 292" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3325_952)">
<rect x="4" width="284" height="284" rx="50" fill="url(#paint0_linear_3325_952)" shape-rendering="crispEdges"/>
<path d="M50.4253 112.314C49.0748 111.587 47.4645 111.224 45.6985 111.224C41.2834 111.224 37.8551 112.314 35.4658 114.548C33.0245 116.782 31.7778 119.69 31.7778 123.222V124.625H29.9079C28.5054 124.625 27.3108 125.092 26.3758 126.027C25.4408 126.962 24.9214 128.157 24.9214 129.559C24.9214 130.962 25.4408 132.157 26.3758 133.092C27.3108 134.027 28.5054 134.494 29.9079 134.494H31.7778V155.063C31.7778 156.518 32.2973 157.764 33.2322 158.803C34.2191 159.842 35.4658 160.361 36.9721 160.361C38.4784 160.361 39.7251 159.842 40.712 158.803C41.6989 157.816 42.1664 156.518 42.1664 155.063V134.494H46.3738C47.7762 134.494 48.9709 134.027 49.9059 133.092C50.8408 132.157 51.3603 130.962 51.3603 129.559C51.3603 128.157 50.8408 126.962 49.9059 126.027C48.9709 125.092 47.7762 124.625 46.3738 124.625H42.1664V123.17C42.1664 122.547 42.3742 121.924 42.7378 121.404C42.9975 120.989 43.6208 120.781 44.5558 120.781C45.0752 120.781 45.8024 120.885 46.6854 121.093L47.4645 121.249C47.7762 121.301 48.0879 121.352 48.3995 121.352C49.6461 121.352 50.7369 120.885 51.62 119.95C52.4511 119.067 52.8666 117.872 52.8666 116.522C52.8666 114.704 51.9836 113.249 50.3733 112.366L50.4253 112.314Z" fill="#00FF00"/>
<path d="M75.982 124.78C74.8393 123.845 73.4888 123.377 71.8785 123.377C69.5931 123.377 67.4634 123.897 65.4896 124.884C64.5027 125.351 63.6196 125.975 62.8924 126.702C62.6847 126.286 62.373 125.923 62.0614 125.559C61.0744 124.52 59.8278 124.001 58.3215 124.001C56.8151 124.001 55.5685 124.52 54.5816 125.559C53.5947 126.546 53.1272 127.845 53.1272 129.299V155.063C53.1272 156.517 53.6466 157.815 54.5816 158.802C55.5685 159.841 56.8151 160.361 58.3215 160.361C59.8278 160.361 61.0744 159.841 62.0614 158.802C63.0483 157.815 63.5158 156.517 63.5158 155.063V140.311C63.5158 138.649 63.7755 137.246 64.3468 136.103C64.8663 135.013 65.5415 134.182 66.3207 133.662C67.0998 133.143 67.827 132.935 68.6061 132.935C68.9697 132.935 69.2814 132.935 69.5931 133.039L70.3203 133.247C71.2552 133.61 72.1383 133.766 72.9693 133.766C74.2679 133.766 75.4106 133.299 76.3456 132.364C77.2806 131.429 77.8 130.078 77.8 128.416C77.8 127.013 77.1767 125.767 75.982 124.78Z" fill="#00FF00"/>
<path d="M102.938 125.819C100.289 124.209 97.4322 123.377 94.4195 123.377C91.4068 123.377 88.498 124.157 85.8489 125.715C83.1999 127.273 81.0183 129.559 79.3561 132.467C77.7459 135.376 76.9148 138.804 76.9148 142.596C76.9148 146.024 77.6939 149.193 79.2003 151.946C80.7586 154.751 82.9402 156.932 85.797 158.543C88.6019 160.101 91.9263 160.932 95.6661 160.932C97.7958 160.932 99.9774 160.568 102.055 159.841C104.185 159.114 105.899 158.231 107.145 157.192C108.652 155.998 109.431 154.595 109.431 152.985C109.431 151.79 108.963 150.647 108.08 149.764C107.197 148.881 106.055 148.414 104.808 148.414C103.925 148.414 102.99 148.674 101.951 149.193L100.757 149.92C100.289 150.232 99.6657 150.492 98.7827 150.751C98.0036 151.011 96.8089 151.167 95.2506 151.167C93.3287 151.167 91.6665 150.595 90.1083 149.401C89.0694 148.57 88.2903 147.531 87.7189 146.284H106.314C107.665 146.284 108.86 145.869 109.898 144.986C110.937 144.103 111.561 142.908 111.613 141.402C111.613 138.181 110.833 135.168 109.223 132.415C107.665 129.663 105.535 127.481 102.886 125.871L102.938 125.819ZM94.4195 133.143C96.0297 133.143 97.4322 133.662 98.7308 134.701C99.5618 135.376 100.133 136.103 100.445 136.986H87.8228C88.8616 134.389 90.9913 133.143 94.4195 133.143Z" fill="#00FF00"/>
<path d="M135.456 139.635C133.898 138.96 131.924 138.285 129.638 137.61C128.08 137.142 126.834 136.779 125.951 136.467C125.275 136.207 124.704 135.896 124.288 135.48C124.081 135.272 123.873 135.013 123.873 134.337C123.873 133.662 123.873 132.363 127.145 132.363C128.444 132.363 129.587 132.571 130.573 133.039C131.56 133.506 132.34 134.078 133.015 134.909C133.898 135.792 134.989 136.207 136.287 136.207C137.378 136.207 138.365 135.896 139.196 135.22C140.339 134.337 140.962 133.091 140.962 131.688C140.962 130.493 140.598 129.455 139.819 128.52C138.469 126.754 136.547 125.455 134.209 124.572C131.924 123.741 129.638 123.273 127.353 123.273C125.068 123.273 122.886 123.741 120.86 124.624C118.782 125.507 117.068 126.857 115.822 128.52C114.471 130.234 113.796 132.311 113.796 134.649C113.796 137.298 114.471 139.428 115.77 141.038C117.016 142.544 118.523 143.739 120.237 144.466C121.795 145.141 123.873 145.869 126.314 146.544C128.496 147.115 130.106 147.738 131.093 148.258C131.716 148.622 131.976 149.037 131.976 149.712C131.976 150.388 131.976 151.79 128.6 151.894C126.886 151.894 125.483 151.634 124.34 151.115C123.146 150.595 122.003 149.712 120.86 148.518C119.769 147.427 118.575 146.907 117.328 146.907C116.445 146.907 115.562 147.167 114.679 147.738C112.809 149.037 112.445 150.595 112.445 151.686C112.445 152.777 112.757 153.712 113.277 154.439C114.939 156.725 117.12 158.335 119.717 159.374C122.263 160.361 125.016 160.828 127.924 160.828C130.106 160.828 132.236 160.413 134.313 159.529C136.443 158.646 138.157 157.348 139.56 155.634C140.962 153.868 141.689 151.738 141.689 149.401C141.689 146.855 141.066 144.726 139.819 143.064C138.625 141.505 137.17 140.311 135.456 139.532V139.635Z" fill="#00FF00"/>
<path d="M163.973 123.378C161.584 123.378 159.402 124.002 157.428 125.196C156.701 125.664 156.026 126.131 155.402 126.651V116.522C155.402 115.067 154.883 113.821 153.948 112.782C152.961 111.743 151.714 111.224 150.208 111.224C148.702 111.224 147.455 111.743 146.468 112.782C145.481 113.769 145.014 115.067 145.014 116.522V155.063C145.014 156.518 145.533 157.816 146.468 158.803C147.455 159.842 148.702 160.361 150.208 160.361C151.714 160.361 152.961 159.842 153.948 158.803C154.935 157.816 155.402 156.518 155.402 155.063V139.117C155.402 137.351 155.922 136 156.961 134.858C157.999 133.767 159.402 133.247 161.324 133.247C163.09 133.247 164.233 133.663 164.804 134.546C165.531 135.585 165.895 137.143 165.895 139.117V155.063C165.895 156.518 166.414 157.816 167.349 158.803C168.336 159.842 169.583 160.361 171.089 160.361C172.595 160.361 173.842 159.842 174.829 158.803C175.816 157.816 176.283 156.518 176.283 155.063V139.117C176.283 134.39 175.4 130.65 173.686 127.897C171.816 124.937 168.544 123.43 163.921 123.43L163.973 123.378Z" fill="#00FF00"/>
<path d="M207.397 124.001C205.891 124.001 204.644 124.521 203.657 125.56C202.67 126.547 202.203 127.845 202.203 129.3V145.246C202.203 147.22 201.735 148.726 200.8 149.713C199.917 150.7 198.619 151.168 196.852 151.168C195.086 151.168 193.736 150.752 192.853 149.869C192.022 149.038 191.554 147.48 191.554 145.246V129.3C191.554 127.845 191.035 126.599 190.1 125.56C189.113 124.521 187.866 124.001 186.36 124.001C184.854 124.001 183.607 124.521 182.62 125.56C181.633 126.547 181.166 127.845 181.166 129.3V147.48C181.166 150.025 181.685 152.362 182.672 154.336C183.711 156.362 185.165 158.024 187.087 159.167C188.957 160.309 191.191 160.933 193.58 160.933C196.904 160.933 199.709 160.258 201.943 158.907C201.683 160.05 201.216 160.881 200.592 161.452C199.502 162.439 197.632 162.959 195.086 162.959C194.307 162.959 193.58 162.855 192.905 162.699C192.178 162.543 191.45 162.335 190.827 162.076L189.217 161.4C188.697 161.244 188.126 161.141 187.555 161.141C186.516 161.141 185.529 161.504 184.698 162.179C183.867 162.855 183.243 163.79 182.932 164.984C182.776 165.504 182.672 165.971 182.672 166.439C182.672 167.945 183.347 170.023 186.62 171.425C187.659 171.841 188.957 172.152 190.515 172.412C192.074 172.672 193.58 172.776 195.086 172.776C200.281 172.776 204.54 171.425 207.709 168.724C210.929 165.971 212.591 161.608 212.591 155.79V129.3C212.591 127.845 212.072 126.599 211.137 125.56C210.15 124.521 208.903 124.001 207.397 124.001Z" fill="white"/>
<path d="M243.341 125.767C240.536 124.157 237.419 123.377 234.043 123.377C230.667 123.377 227.498 124.209 224.693 125.767C221.888 127.377 219.655 129.611 218.045 132.467C216.434 135.324 215.603 138.545 215.603 142.129C215.603 145.713 216.434 148.933 218.045 151.79C219.655 154.647 221.888 156.881 224.693 158.491C227.498 160.049 230.615 160.88 234.043 160.88C237.471 160.88 240.536 160.101 243.341 158.491C246.146 156.932 248.431 154.647 250.041 151.79C251.652 148.933 252.483 145.713 252.483 142.129C252.483 138.545 251.652 135.324 250.041 132.467C248.431 129.611 246.146 127.325 243.341 125.767ZM240.951 146.908C240.224 148.258 239.289 149.297 238.043 150.024C236.848 150.751 235.549 151.115 234.043 151.115C232.537 151.115 231.238 150.751 230.043 150.024C228.849 149.297 227.862 148.258 227.135 146.908C226.407 145.557 226.044 143.947 226.044 142.181C226.044 140.415 226.407 138.804 227.135 137.402C227.862 136.051 228.797 135.013 230.043 134.233C231.238 133.506 232.537 133.143 234.043 133.143C235.549 133.143 236.848 133.506 238.043 134.233C239.237 134.961 240.224 136 240.951 137.35C241.679 138.753 242.042 140.311 242.042 142.129C242.042 143.947 241.679 145.505 240.951 146.856V146.908Z" fill="white"/>
<path d="M261.52 146.545C263.65 146.545 265.052 145.142 265.208 142.805L266.974 119.898C267.13 118.184 266.662 116.729 265.623 115.535C264.533 114.34 263.13 113.717 261.468 113.717C259.806 113.717 258.403 114.34 257.364 115.535C256.326 116.729 255.858 118.132 255.962 119.846L257.78 142.805C257.988 145.142 259.39 146.545 261.572 146.545H261.52Z" fill="white"/>
<path d="M265.676 150.234C264.741 149.299 263.443 148.832 261.884 148.832H261.053C259.443 148.832 258.144 149.299 257.261 150.234C256.326 151.169 255.859 152.468 255.859 154.026V155.065C255.859 156.675 256.326 157.973 257.261 158.856C258.196 159.791 259.495 160.259 261.053 160.259H261.884C263.494 160.259 264.793 159.791 265.676 158.856C266.559 157.922 267.079 156.623 267.079 155.065V154.026C267.079 152.416 266.611 151.117 265.676 150.234Z" fill="white"/>
</g>
<defs>
<filter id="filter0_d_3325_952" x="0" y="0" width="292" height="292" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3325_952"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3325_952" result="shape"/>
</filter>
<linearGradient id="paint0_linear_3325_952" x1="107.591" y1="-51.616" x2="176.735" y2="284" gradientUnits="userSpaceOnUse">
<stop stop-color="#065FFB"/>
<stop offset="1" stop-color="#001335"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 MiB

View file

@ -1,143 +0,0 @@
import React, { useState, useRef, useEffect } from 'react';
import { View, Dimensions, Image, ScrollView, NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
import { MyTouchableOpacity, MyText, tw } from 'common-ui';
import { useRouter } from 'expo-router';
import { useBanners } from '@/src/hooks/prominent-api-hooks';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
const { width: screenWidth } = Dimensions.get('window');
interface Banner {
id: number;
name: string;
imageUrl: string;
description?: string | null;
skuIds?: number[] | null;
redirectUrl?: string | null;
serialNum?: number | null;
isActive: boolean;
}
export default function BannerCarousel() {
const router = useRouter();
const scrollViewRef = useRef<ScrollView>(null);
const [currentIndex, setCurrentIndex] = useState(0);
const [isAutoPlaying, setIsAutoPlaying] = useState(true);
// Fetch banners data
const { data: bannersData, isLoading, error } = useBanners();
const banners = bannersData?.banners || [];
// Auto-play functionality
useEffect(() => {
if (banners.length <= 1 || !isAutoPlaying) return; // Don't auto-play if conditions not met
const interval = setInterval(() => {
setCurrentIndex((prevIndex) => {
const nextIndex = (prevIndex + 1) % banners.length;
// Auto-scroll to next slide
if (scrollViewRef.current) {
scrollViewRef.current.scrollTo({
x: nextIndex * (screenWidth - 32),
animated: true,
});
}
return nextIndex;
});
}, 6000); // 6 seconds
return () => clearInterval(interval); // Cleanup on unmount
}, [banners.length, isAutoPlaying]);
if (isLoading) {
return (
<View style={tw`px-4 py-4 bg-gradient-to-r from-pink-500 to-rose-500 items-center justify-center h-48`}>
<MyText style={tw`text-white`}>Loading banners...</MyText>
</View>
);
}
if (error || !banners || banners.length === 0) return null;
const handleBannerPress = (banner: Banner) => {
if (banner.skuIds && banner.skuIds.length > 0) {
// Navigate to the first product's detail page
router.push(`/(drawer)/(tabs)/home/product-detail/${banner.skuIds[0]}`);
} else if (banner.redirectUrl) {
// Handle external URL - could open in browser or handle deep links
}
// If no skuIds or redirectUrl, banner is just for display
};
const handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
const slideSize = screenWidth - 32; // width minus horizontal padding
const index = Math.round(event.nativeEvent.contentOffset.x / slideSize);
setCurrentIndex(index);
};
const goToSlide = (index: number) => {
setIsAutoPlaying(false); // Pause auto-play when user manually navigates
if (scrollViewRef.current) {
scrollViewRef.current.scrollTo({
x: index * (screenWidth - 32),
animated: true,
});
}
setCurrentIndex(index);
// Resume auto-play after a short delay
setTimeout(() => setIsAutoPlaying(true), 1000);
};
return (
<View style={tw`px-4 py-4 bg-gradient-to-r from-pink-500 to-rose-500`}>
<ScrollView
ref={scrollViewRef}
horizontal
showsHorizontalScrollIndicator={false}
pagingEnabled
decelerationRate="fast"
snapToInterval={screenWidth - 32}
onScroll={handleScroll}
scrollEventThrottle={16}
onTouchStart={() => setIsAutoPlaying(false)}
onTouchEnd={() => setIsAutoPlaying(true)}
onMomentumScrollEnd={() => setIsAutoPlaying(true)}
>
{banners.map((banner: Banner) => (
<MyTouchableOpacity
key={banner.id}
onPress={() => handleBannerPress(banner)}
style={tw`mr-4 rounded-2xl overflow-hidden`}
activeOpacity={0.9}
>
<Image
source={{ uri: banner.imageUrl }}
style={tw`w-[${screenWidth - 64}px] h-48 rounded-2xl`}
resizeMode="cover"
/>
</MyTouchableOpacity>
))}
</ScrollView>
{/* Pagination Dots */}
{banners.length > 1 && (
<View style={tw`flex-row justify-center mt-3`}>
{banners.map((_: Banner, index: number) => (
<MyTouchableOpacity
key={index}
onPress={() => goToSlide(index)}
style={tw`mx-1`}
>
<View
style={tw`w-2 h-2 rounded-full ${
index === currentIndex ? 'bg-gray-800' : 'bg-gray-400'
}`}
/>
</MyTouchableOpacity>
))}
</View>
)}
</View>
);
}

View file

@ -94,15 +94,6 @@ const PaymentAndOrderComponent: React.FC<PaymentAndOrderProps> = ({
}, },
}); });
// const createRazorpayOrderMutation = trpc.user.payment.createRazorpayOrder.useMutation({
// onSuccess: (paymentData) => {
// initiateRazorpayPayment(paymentData.razorpayOrderId, paymentData.key, finalTotal);
// },
// onError: (error: any) => {
// Alert.alert('Error', error.message || 'Failed to create payment order');
// },
// });
const verifyPaymentMutation = trpc.user.payment.verifyPayment.useMutation({ const verifyPaymentMutation = trpc.user.payment.verifyPayment.useMutation({
onSuccess: () => { onSuccess: () => {
const orders = placeOrderMutation.data?.data || []; const orders = placeOrderMutation.data?.data || [];
@ -167,51 +158,6 @@ const PaymentAndOrderComponent: React.FC<PaymentAndOrderProps> = ({
placeOrderMutation.mutate(orderData); placeOrderMutation.mutate(orderData);
}; };
// const initiateRazorpayPayment = (razorpayOrderId: string, key: string, amount: number) => {
// const options = {
// key,
// amount: amount * 100,
// currency: 'INR',
// order_id: razorpayOrderId,
// name: 'Meat Farmer',
// description: 'Order Payment',
// prefill: {},
// };
// RazorpayCheckout.open(options)
// .then((data: any) => {
// verifyPaymentMutation.mutate({
// razorpay_payment_id: data.razorpay_payment_id,
// razorpay_order_id: data.razorpay_order_id,
// razorpay_signature: data.razorpay_signature,
// });
// })
// .catch((error: any) => {
// markPaymentFailedMutation.mutate({ merchantOrderId: razorpayOrderId });
// Alert.alert(
// 'Payment Failed',
// 'Payment failed or was cancelled. What would you like to do?',
// [
// {
// text: 'Retry Now',
// onPress: () => {
// const orders = placeOrderMutation.data?.data || [];
// const firstOrder = orders[0];
// const orderId = firstOrder?.id.toString();
// if (orderId) {
// createRazorpayOrderMutation.mutate({ orderId });
// }
// }
// },
// {
// text: 'Retry Later',
// onPress: () => router.push('/(drawer)/(tabs)/me/my-orders')
// }
// ]
// );
// });
// };
return ( return (
<> <>
{/* Back Button */} {/* Back Button */}

View file

@ -1,26 +0,0 @@
/**
* Below are the colors that are used in the app. The colors are defined in the light and dark mode.
* There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
*/
const tintColorLight = '#0a7ea4';
const tintColorDark = '#fff';
export const Colors = {
light: {
text: '#11181C',
background: '#fff',
tint: tintColorLight,
icon: '#687076',
tabIconDefault: '#687076',
tabIconSelected: tintColorLight,
},
dark: {
text: '#ECEDEE',
background: '#151718',
tint: tintColorDark,
icon: '#9BA1A6',
tabIconDefault: '#9BA1A6',
tabIconSelected: tintColorDark,
},
};

View file

@ -2,7 +2,6 @@
import {StorageService} from 'common-ui'; import {StorageService} from 'common-ui';
export const AUTH_TOKEN_KEY = 'authToken'; export const AUTH_TOKEN_KEY = 'authToken';
export const ROLES_KEY = 'user_roles';
export const USER_ID_KEY = 'userId'; export const USER_ID_KEY = 'userId';
export async function saveUserId(userId:string) { export async function saveUserId(userId:string) {
@ -24,26 +23,3 @@ export async function getAuthToken() {
export async function deleteAuthToken() { export async function deleteAuthToken() {
await StorageService.removeItem(AUTH_TOKEN_KEY); await StorageService.removeItem(AUTH_TOKEN_KEY);
} }
export async function saveRoles(roles: string[]) {
await StorageService.setItem(ROLES_KEY, JSON.stringify(roles));
}
export async function getRoles(): Promise<string[] | null> {
const token = await getAuthToken();
if (!token) {
StorageService.removeItem(ROLES_KEY);
return null;
}
const rolesStr = await StorageService.getItem(ROLES_KEY);
if (!rolesStr) return null;
try {
return JSON.parse(rolesStr);
} catch {
return null;
}
}
export async function deleteRoles() {
await StorageService.removeItem(ROLES_KEY);
}

View file

@ -2,40 +2,6 @@ import Toast from "react-native-toast-message";
import React from "react"; import React from "react";
import { useRouter } from "expo-router"; import { useRouter } from "expo-router";
export function InfoToast(message: string) {
Toast.show({
type: "info",
text1: message,
position: "top",
visibilityTime: 10000,
onPress: () => {
Toast.hide();
},
});
}
export function ErrorToast(message: string) {
Toast.show({
type: "error",
text1: message,
position: "top",
onPress: () => {
Toast.hide();
},
});
}
export function SuccessToast(message: string) {
Toast.show({
type: "success",
text1: message,
position: "top",
onPress: () => {
Toast.hide();
},
});
}
export function NotificationToast(title: string, subtitle: string, data: any) { export function NotificationToast(title: string, subtitle: string, data: any) {
const router = useRouter(); const router = useRouter();
Toast.show({ Toast.show({

View file

@ -1,38 +0,0 @@
import * as React from 'react';
import * as AuthSession from 'expo-auth-session';
import * as WebBrowser from 'expo-web-browser';
import { Button, View } from 'react-native';
import * as Google from 'expo-auth-session/providers/google';
WebBrowser.maybeCompleteAuthSession();
const discovery = {
authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth',
tokenEndpoint: 'https://oauth2.googleapis.com/token',
};
WebBrowser.maybeCompleteAuthSession();
export default function GoogleSignInPKCE() {
// const [request, response, promptAsync] = Google.useAuthRequest({
// androidClientId: androidClientId,
// iosClientId: iosClientId,
// webClientId: webClientId,
// redirectUri: 'https://www.freshyo.in/oauthredirect',
// // redirectUri: AuthSession.makeRedirectUri({ scheme: 'freshyo', path: 'oauthredirect' }),
// scopes: ["openid", "profile", "email"],
// });
return (
<View style={{ marginTop: 100 }}>
<Button title="Sign in with Google" onPress={() => {}} />
{/* <Button title="Sign in with Google" onPress={() => promptAsync()} /> */}
</View>
);
}

View file

@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import axios from 'axios' import axios from 'axios'
import { trpc } from '@/src/trpc-client' import { trpc } from '@/src/trpc-client'
import { AllProductsApiType, StoresApiType, SlotsApiType, EssentialConstsApiType, BannersApiType, StoreWithProductsApiType, AvailabilityApiType } from "@backend/trpc/router"; import { AllProductsApiType, StoresApiType, SlotsApiType, EssentialConstsApiType, StoreWithProductsApiType, AvailabilityApiType } from "@backend/trpc/router";
import { CACHE_FILENAMES } from "@packages/shared"; import { CACHE_FILENAMES } from "@packages/shared";
import { StorageServiceCasual } from 'common-ui'; import { StorageServiceCasual } from 'common-ui';
@ -52,7 +52,6 @@ export const useGetEssentialConsts = () => {
type ProductsResponse = AllProductsApiType; type ProductsResponse = AllProductsApiType;
type StoresResponse = StoresApiType; type StoresResponse = StoresApiType;
type SlotsResponse = SlotsApiType; type SlotsResponse = SlotsApiType;
type BannersResponse = BannersApiType;
type StoreWithProductsResponse = StoreWithProductsApiType; type StoreWithProductsResponse = StoreWithProductsApiType;
type AvailabilityResponse = AvailabilityApiType; type AvailabilityResponse = AvailabilityApiType;
@ -219,24 +218,6 @@ export function useSlots() {
}) })
} }
export function useBanners() {
const cacheUrl = useCacheUrl(CACHE_FILENAMES.banners)
const version = cacheUrl ?? ''
return useQuery<BannersResponse>({
queryKey: ['banners', version],
queryFn: async () => {
if (!cacheUrl) {
throw new Error('Cache URL not available')
}
const response = await axios.get<BannersResponse>(cacheUrl)
return response.data
},
staleTime: 60000, // 1 minute
enabled: !!cacheUrl,
})
}
export function useStoreWithProducts(storeId: number) { export function useStoreWithProducts(storeId: number) {
const { data: essentialConsts } = useGetEssentialConsts() const { data: essentialConsts } = useGetEssentialConsts()

View file

@ -3,11 +3,9 @@ import { create } from 'zustand';
interface AddressState { interface AddressState {
selectedAddressId: number | null; selectedAddressId: number | null;
setSelectedAddressId: (addressId: number | null) => void; setSelectedAddressId: (addressId: number | null) => void;
clearSelectedAddress: () => void;
} }
export const useAddressStore = create<AddressState>((set) => ({ export const useAddressStore = create<AddressState>((set) => ({
selectedAddressId: null, selectedAddressId: null,
setSelectedAddressId: (addressId) => set({ selectedAddressId: addressId }), setSelectedAddressId: (addressId) => set({ selectedAddressId: addressId }),
clearSelectedAddress: () => set({ selectedAddressId: null }),
})); }));

View file

@ -9,7 +9,6 @@ interface CentralProductState {
productsById: Record<number, Product> productsById: Record<number, Product>
refetchProducts: (() => Promise<void>) | null refetchProducts: (() => Promise<void>) | null
setProducts: (products: Product[]) => void setProducts: (products: Product[]) => void
clearProducts: () => void
setRefetchProducts: (refetch: () => Promise<void>) => void setRefetchProducts: (refetch: () => Promise<void>) => void
} }
@ -26,7 +25,6 @@ export const useCentralProductStore = create<CentralProductState>((set) => ({
set({ products, productsById }) set({ products, productsById })
}, },
clearProducts: () => set({ products: [], productsById: {} }),
setRefetchProducts: (refetchProducts) => set({ refetchProducts }), setRefetchProducts: (refetchProducts) => set({ refetchProducts }),
})) }))

View file

@ -20,7 +20,6 @@ interface CentralSlotState {
isSlotsLoaded: boolean; isSlotsLoaded: boolean;
refetchSlots: (() => Promise<void>) | null; refetchSlots: (() => Promise<void>) | null;
setSlotsData: (slots: Slot[], productAvailability: ProductAvailability[], availability: AvailabilityEntry[]) => void; setSlotsData: (slots: Slot[], productAvailability: ProductAvailability[], availability: AvailabilityEntry[]) => void;
clearSlotsData: () => void;
setRefetchSlots: (refetch: () => Promise<void>) => void; setRefetchSlots: (refetch: () => Promise<void>) => void;
} }
@ -57,7 +56,6 @@ export const useCentralSlotStore = create<CentralSlotState>((set) => ({
set({ slots, productSlotsMap, isSlotsLoaded: true }); set({ slots, productSlotsMap, isSlotsLoaded: true });
}, },
clearSlotsData: () => set({ slots: [], productSlotsMap: {}, isSlotsLoaded: false }),
setRefetchSlots: (refetchSlots) => set({ refetchSlots }), setRefetchSlots: (refetchSlots) => set({ refetchSlots }),
})); }));

View file

@ -2,14 +2,12 @@ import { create } from 'zustand';
interface QuickDeliveryState { interface QuickDeliveryState {
isDrawerHidden: boolean; isDrawerHidden: boolean;
setDrawerHidden: (hidden: boolean) => void;
selectedSlotId: number | null; selectedSlotId: number | null;
setSelectedSlotId: (slotId: number | null) => void; setSelectedSlotId: (slotId: number | null) => void;
} }
export const useQuickDeliveryStore = create<QuickDeliveryState>((set) => ({ export const useQuickDeliveryStore = create<QuickDeliveryState>((set) => ({
isDrawerHidden: false, isDrawerHidden: false,
setDrawerHidden: (hidden: boolean) => set({ isDrawerHidden: hidden }),
selectedSlotId: null, selectedSlotId: null,
setSelectedSlotId: (slotId) => set({ selectedSlotId: slotId }), setSelectedSlotId: (slotId) => set({ selectedSlotId: slotId }),
})); }));

214
bun.lock
View file

@ -49,7 +49,7 @@
"@react-navigation/elements": "^2.3.8", "@react-navigation/elements": "^2.3.8",
"@react-navigation/material-top-tabs": "^7.4.11", "@react-navigation/material-top-tabs": "^7.4.11",
"@react-navigation/native": "^7.1.6", "@react-navigation/native": "^7.1.6",
"@tanstack/react-query": "^5.85.9", "@tanstack/react-query": "^5.100.0",
"@trpc/client": "^11.6.0", "@trpc/client": "^11.6.0",
"@trpc/react-query": "^11.6.0", "@trpc/react-query": "^11.6.0",
"axios": "^1.11.0", "axios": "^1.11.0",
@ -143,7 +143,7 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-slot": "^1.1.2",
"@tanstack/react-query": "^5.59.16", "@tanstack/react-query": "^5.100.0",
"@tanstack/react-router": "^1.92.8", "@tanstack/react-router": "^1.92.8",
"@tanstack/router-devtools": "^1.92.8", "@tanstack/router-devtools": "^1.92.8",
"@trpc/client": "^11.6.0", "@trpc/client": "^11.6.0",
@ -202,7 +202,7 @@
"@react-navigation/drawer": "^7.3.9", "@react-navigation/drawer": "^7.3.9",
"@react-navigation/elements": "^2.3.8", "@react-navigation/elements": "^2.3.8",
"@react-navigation/native": "^7.1.6", "@react-navigation/native": "^7.1.6",
"@tanstack/react-query": "^5.85.9", "@tanstack/react-query": "^5.100.0",
"@trpc/client": "^11.6.0", "@trpc/client": "^11.6.0",
"@trpc/react-query": "^11.6.0", "@trpc/react-query": "^11.6.0",
"axios": "^1.11.0", "axios": "^1.11.0",
@ -369,7 +369,7 @@
"@react-navigation/drawer": "^7.5.8", "@react-navigation/drawer": "^7.5.8",
"@react-navigation/elements": "^2.3.8", "@react-navigation/elements": "^2.3.8",
"@react-navigation/native": "^7.1.6", "@react-navigation/native": "^7.1.6",
"@tanstack/react-query": "^5.85.9", "@tanstack/react-query": "^5.100.0",
"axios": "^1.11.0", "axios": "^1.11.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"dayjs": "^1.11.18", "dayjs": "^1.11.18",
@ -1207,17 +1207,17 @@
"@tanstack/history": ["@tanstack/history@1.161.6", "", {}, "sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg=="], "@tanstack/history": ["@tanstack/history@1.161.6", "", {}, "sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg=="],
"@tanstack/query-core": ["@tanstack/query-core@5.90.20", "", {}, "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg=="], "@tanstack/query-core": ["@tanstack/query-core@5.100.9", "", {}, "sha512-SJSFw1S8+kQ0+knv/XGfrbocWoAlT7vDKsSImtLx3ZPQmEcR46hkDjLSvynSy25N8Ms4tIEini1FuBd5k7IscQ=="],
"@tanstack/query-devtools": ["@tanstack/query-devtools@5.100.9", "", {}, "sha512-gqiptrTIhbK2PuCaPRHmWXfJG1NGYVFpAr0HqogEqiSBNB5xDz6fmesQt7w4WgMOqOQPnPHJ3ZDMuhDaXvNO8g=="], "@tanstack/query-devtools": ["@tanstack/query-devtools@5.100.9", "", {}, "sha512-gqiptrTIhbK2PuCaPRHmWXfJG1NGYVFpAr0HqogEqiSBNB5xDz6fmesQt7w4WgMOqOQPnPHJ3ZDMuhDaXvNO8g=="],
"@tanstack/react-devtools": ["@tanstack/react-devtools@0.10.2", "", { "dependencies": { "@tanstack/devtools": "0.11.2" }, "peerDependencies": { "@types/react": ">=16.8", "@types/react-dom": ">=16.8", "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-1BmZyxOrI5SqmRJ5MgkYZNNdnlLsJxQRI2YgorrAvcF2MxK6x5RcuStvD8+YlXoMw3JtNukPxoITirKAnKYDQA=="], "@tanstack/react-devtools": ["@tanstack/react-devtools@0.10.2", "", { "dependencies": { "@tanstack/devtools": "0.11.2" }, "peerDependencies": { "@types/react": ">=16.8", "@types/react-dom": ">=16.8", "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-1BmZyxOrI5SqmRJ5MgkYZNNdnlLsJxQRI2YgorrAvcF2MxK6x5RcuStvD8+YlXoMw3JtNukPxoITirKAnKYDQA=="],
"@tanstack/react-query": ["@tanstack/react-query@5.90.21", "", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg=="], "@tanstack/react-query": ["@tanstack/react-query@5.100.9", "", { "dependencies": { "@tanstack/query-core": "5.100.9" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-Oa44XkaI3kCNN6ME0KByU3xT3SEUNOMfZpHxL6+wFoTm+OeUFYHKdeYVe0aOXlRDm/f15sgLwEt2HDorIdW8+A=="],
"@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.100.9", "", { "dependencies": { "@tanstack/query-devtools": "5.100.9" }, "peerDependencies": { "@tanstack/react-query": "^5.100.9", "react": "^18 || ^19" } }, "sha512-mM3slaVGXJmz+pOLgXdANj75ikgQCyudyl3kmFvm6brI1JyVeY/+IeD17uDHIvZrD8hfoO2sdZ54RFsHdYAuhA=="], "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.100.9", "", { "dependencies": { "@tanstack/query-devtools": "5.100.9" }, "peerDependencies": { "@tanstack/react-query": "^5.100.9", "react": "^18 || ^19" } }, "sha512-mM3slaVGXJmz+pOLgXdANj75ikgQCyudyl3kmFvm6brI1JyVeY/+IeD17uDHIvZrD8hfoO2sdZ54RFsHdYAuhA=="],
"@tanstack/react-router": ["@tanstack/react-router@1.167.3", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.1", "@tanstack/router-core": "1.167.3", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-1qbSy4r+O7IBdmPLlcKsjB041Gq2MMnIEAYSGIjaMZIL4duUIQnOWLw4jTfjKil/IJz/9rO5JcvrbxOG5UTSdg=="], "@tanstack/react-router": ["@tanstack/react-router@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.169.2", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-OJM7Kguc7ERnweaNRWsyWgIKcl3z23rD1B4jaxjzd9RGdnzpt2HfrWa9rggbT0Hfzhfo4D2ZmsfoTme035tniQ=="],
"@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.166.13", "", { "dependencies": { "@tanstack/router-devtools-core": "1.167.3" }, "peerDependencies": { "@tanstack/react-router": "^1.168.15", "@tanstack/router-core": "^1.168.11", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-6yKRFFJrEEOiGp5RAAuGCYsl81M4XAhJmLcu9PKj+HZle4A3dsP60lwHoqQYWHMK9nKKFkdXR+D8qxzxqtQbEA=="], "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.166.13", "", { "dependencies": { "@tanstack/router-devtools-core": "1.167.3" }, "peerDependencies": { "@tanstack/react-router": "^1.168.15", "@tanstack/router-core": "^1.168.11", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-6yKRFFJrEEOiGp5RAAuGCYsl81M4XAhJmLcu9PKj+HZle4A3dsP60lwHoqQYWHMK9nKKFkdXR+D8qxzxqtQbEA=="],
@ -1231,9 +1231,9 @@
"@tanstack/react-start-server": ["@tanstack/react-start-server@1.166.52", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-router": "1.169.2", "@tanstack/router-core": "1.169.2", "@tanstack/start-client-core": "1.168.2", "@tanstack/start-server-core": "1.167.30" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-46Gx+byIndYywUtyna5h3qatHipJkPFqo/miexfuYPgeVAI6ypQzsw7wxF194H6VAP43m2q+fdLPBXStufoOGw=="], "@tanstack/react-start-server": ["@tanstack/react-start-server@1.166.52", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-router": "1.169.2", "@tanstack/router-core": "1.169.2", "@tanstack/start-client-core": "1.168.2", "@tanstack/start-server-core": "1.167.30" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-46Gx+byIndYywUtyna5h3qatHipJkPFqo/miexfuYPgeVAI6ypQzsw7wxF194H6VAP43m2q+fdLPBXStufoOGw=="],
"@tanstack/react-store": ["@tanstack/react-store@0.9.2", "", { "dependencies": { "@tanstack/store": "0.9.2", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Vt5usJE5sHG/cMechQfmwvwne6ktGCELe89Lmvoxe3LKRoFrhPa8OCKWs0NliG8HTJElEIj7PLtaBQIcux5pAQ=="], "@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="],
"@tanstack/router-core": ["@tanstack/router-core@1.167.3", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/store": "^0.9.1", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-M/CxrTGKk1fsySJjd+Pzpbi3YLDz+cJSutDjSTMy12owWlOgHV/I6kzR0UxyaBlHraM6XgMHNA0XdgsS1fa4Nw=="], "@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@tanstack/router-devtools": ["@tanstack/router-devtools@1.166.9", "", { "dependencies": { "@tanstack/react-router-devtools": "1.166.9", "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/react-router": "^1.167.2", "csstype": "^3.0.10", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["csstype"] }, "sha512-AOZWjCju4jpEw/zE/hapo5slmM7RTAGMB5zNeo60ZOFr+Tkrt0utxOGTJ0mVDTKyBA1DGnyryAced3rBulT19A=="], "@tanstack/router-devtools": ["@tanstack/router-devtools@1.166.9", "", { "dependencies": { "@tanstack/react-router-devtools": "1.166.9", "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/react-router": "^1.167.2", "csstype": "^3.0.10", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["csstype"] }, "sha512-AOZWjCju4jpEw/zE/hapo5slmM7RTAGMB5zNeo60ZOFr+Tkrt0utxOGTJ0mVDTKyBA1DGnyryAced3rBulT19A=="],
@ -1257,13 +1257,13 @@
"@tanstack/start-storage-context": ["@tanstack/start-storage-context@1.166.35", "", { "dependencies": { "@tanstack/router-core": "1.169.2" } }, "sha512-ZKDkKiorJrKwfEHjatEwRHG7EP3raJPhh6CSl4CFmHW0naIvwaW5gQcxcT8IlHtoGDLYDAjBEcSr3MZyXgqmOA=="], "@tanstack/start-storage-context": ["@tanstack/start-storage-context@1.166.35", "", { "dependencies": { "@tanstack/router-core": "1.169.2" } }, "sha512-ZKDkKiorJrKwfEHjatEwRHG7EP3raJPhh6CSl4CFmHW0naIvwaW5gQcxcT8IlHtoGDLYDAjBEcSr3MZyXgqmOA=="],
"@tanstack/store": ["@tanstack/store@0.9.2", "", {}, "sha512-K013lUJEFJK2ofFQ/hZKJUmCnpcV00ebLyOyFOWQvyQHUOZp/iYO84BM6aOGiV81JzwbX0APTVmW8YI7yiG5oA=="], "@tanstack/store": ["@tanstack/store@0.9.3", "", {}, "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw=="],
"@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.161.7", "", { "bin": { "intent": "bin/intent.js" } }, "sha512-olW33+Cn+bsCsZKPwEGhlkqS6w3M2slFv11JIobdnCFKMLG97oAI2kWKdx5/zsywTL8flpnoIgaZZPlQTFYhdQ=="], "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.161.7", "", { "bin": { "intent": "bin/intent.js" } }, "sha512-olW33+Cn+bsCsZKPwEGhlkqS6w3M2slFv11JIobdnCFKMLG97oAI2kWKdx5/zsywTL8flpnoIgaZZPlQTFYhdQ=="],
"@trpc/client": ["@trpc/client@11.13.4", "", { "peerDependencies": { "@trpc/server": "11.13.4", "typescript": ">=5.7.2" } }, "sha512-AOM7u2blAjjpAzEyDXm4bk8f1HML0sFLuSXPsqZHQX3XCIVo7+mlArEAGMbwEtTwW8hUocI0i3/9tVYUF4Nu0g=="], "@trpc/client": ["@trpc/client@11.17.0", "", { "peerDependencies": { "@trpc/server": "11.17.0", "typescript": ">=5.7.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-KpJBFrbKTDeVCFv/3ckL1XBBH5Yssn8hethI/rUy7GIpTj+VzjtPjykDqJpzobuVOz+d26cXCSu1t4I6MYI5Zg=="],
"@trpc/react-query": ["@trpc/react-query@11.13.4", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.13.4", "@trpc/server": "11.13.4", "react": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-YGdm8zl2iypiO3Uyw99oToGgnZ51BMoT0YLPkLod0bT7QqJX2oHzBzIBFOOfMupyLEH1pzdEC27F+fw9sAtQsA=="], "@trpc/react-query": ["@trpc/react-query@11.17.0", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.17.0", "@trpc/server": "11.17.0", "react": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-AGcl5YAF8NnhBmyJ6PqJqKb1M5VTGSoNRNqJ3orct4o4epdcg0GWhW+qT9q6gPzs/2ImIwYCdfFpgNGdZ9yLHA=="],
"@trpc/server": ["@trpc/server@11.17.0", "", { "peerDependencies": { "typescript": ">=5.7.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-jbAOUe0PpUTCYqziyu+8vYXZdDXPudZgnEhWCQ2NjKnVEjfE93RqHTt1oycZJv/HNf51YlRXfEEwSIAbb161rw=="], "@trpc/server": ["@trpc/server@11.17.0", "", { "peerDependencies": { "typescript": ">=5.7.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-jbAOUe0PpUTCYqziyu+8vYXZdDXPudZgnEhWCQ2NjKnVEjfE93RqHTt1oycZJv/HNf51YlRXfEEwSIAbb161rw=="],
@ -1529,7 +1529,7 @@
"aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], "aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="],
"axios": ["axios@1.13.6", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ=="], "axios": ["axios@1.16.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w=="],
"babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="], "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="],
@ -1699,7 +1699,7 @@
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
"cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="], "cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="], "cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="],
@ -2055,7 +2055,7 @@
"flow-enums-runtime": ["flow-enums-runtime@0.0.6", "", {}, "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw=="], "flow-enums-runtime": ["flow-enums-runtime@0.0.6", "", {}, "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw=="],
"follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], "follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
"fontfaceobserver": ["fontfaceobserver@2.3.0", "", {}, "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg=="], "fontfaceobserver": ["fontfaceobserver@2.3.0", "", {}, "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg=="],
@ -2681,7 +2681,7 @@
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
"postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], "postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="],
"postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="],
@ -2739,7 +2739,7 @@
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], "proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
"pug": ["pug@3.0.4", "", { "dependencies": { "pug-code-gen": "^3.0.4", "pug-filters": "^4.0.0", "pug-lexer": "^5.0.1", "pug-linker": "^4.0.0", "pug-load": "^3.0.0", "pug-parser": "^6.0.0", "pug-runtime": "^3.0.1", "pug-strip-comments": "^2.0.0" } }, "sha512-kFfq5mMzrS7+wrl5pLJzZEzemx34OQ0w4SARfhy/3yxTlhbstsudDwJzhf1hP02yHzbjoVMSXUj/Sz6RNfMyXg=="], "pug": ["pug@3.0.4", "", { "dependencies": { "pug-code-gen": "^3.0.4", "pug-filters": "^4.0.0", "pug-lexer": "^5.0.1", "pug-linker": "^4.0.0", "pug-load": "^3.0.0", "pug-parser": "^6.0.0", "pug-runtime": "^3.0.1", "pug-strip-comments": "^2.0.0" } }, "sha512-kFfq5mMzrS7+wrl5pLJzZEzemx34OQ0w4SARfhy/3yxTlhbstsudDwJzhf1hP02yHzbjoVMSXUj/Sz6RNfMyXg=="],
@ -2937,9 +2937,9 @@
"serialize-error": ["serialize-error@2.1.0", "", {}, "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw=="], "serialize-error": ["serialize-error@2.1.0", "", {}, "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw=="],
"seroval": ["seroval@1.5.1", "", {}, "sha512-OwrZRZAfhHww0WEnKHDY8OM0U/Qs8OTfIDWhUD4BLpNJUfXK4cGmjiagGze086m+mhI+V2nD0gfbHEnJjb9STA=="], "seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"seroval-plugins": ["seroval-plugins@1.5.1", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-4FbuZ/TMl02sqv0RTFexu0SP6V+ywaIe5bAWCCEik0fk17BhALgwvUDVF7e3Uvf9pxmwCEJsRPmlkUE6HdzLAw=="], "seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="], "serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="],
@ -3535,56 +3535,26 @@
"@tanstack/devtools-vite/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "@tanstack/devtools-vite/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
"@tanstack/react-query-devtools/@tanstack/react-query": ["@tanstack/react-query@5.100.9", "", { "dependencies": { "@tanstack/query-core": "5.100.9" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-Oa44XkaI3kCNN6ME0KByU3xT3SEUNOMfZpHxL6+wFoTm+OeUFYHKdeYVe0aOXlRDm/f15sgLwEt2HDorIdW8+A=="],
"@tanstack/react-start/@tanstack/react-router": ["@tanstack/react-router@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.169.2", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-OJM7Kguc7ERnweaNRWsyWgIKcl3z23rD1B4jaxjzd9RGdnzpt2HfrWa9rggbT0Hfzhfo4D2ZmsfoTme035tniQ=="],
"@tanstack/react-start-client/@tanstack/react-router": ["@tanstack/react-router@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.169.2", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-OJM7Kguc7ERnweaNRWsyWgIKcl3z23rD1B4jaxjzd9RGdnzpt2HfrWa9rggbT0Hfzhfo4D2ZmsfoTme035tniQ=="],
"@tanstack/react-start-client/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@tanstack/react-start-rsc/@tanstack/react-router": ["@tanstack/react-router@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.169.2", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-OJM7Kguc7ERnweaNRWsyWgIKcl3z23rD1B4jaxjzd9RGdnzpt2HfrWa9rggbT0Hfzhfo4D2ZmsfoTme035tniQ=="],
"@tanstack/react-start-rsc/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@tanstack/react-start-server/@tanstack/react-router": ["@tanstack/react-router@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.169.2", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-OJM7Kguc7ERnweaNRWsyWgIKcl3z23rD1B4jaxjzd9RGdnzpt2HfrWa9rggbT0Hfzhfo4D2ZmsfoTme035tniQ=="],
"@tanstack/react-start-server/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@tanstack/router-devtools/@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.166.9", "", { "dependencies": { "@tanstack/router-devtools-core": "1.166.9" }, "peerDependencies": { "@tanstack/react-router": "^1.167.2", "@tanstack/router-core": "^1.167.2", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-O49eZmaeEKB5YnKH/qd61AbxV/lW8ICm4stfZ4GNQNpzQQ6rhPIB0p3PMZDIgX+6DoMivdNvLRmXAOOpzpIpDg=="], "@tanstack/router-devtools/@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.166.9", "", { "dependencies": { "@tanstack/router-devtools-core": "1.166.9" }, "peerDependencies": { "@tanstack/react-router": "^1.167.2", "@tanstack/router-core": "^1.167.2", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-O49eZmaeEKB5YnKH/qd61AbxV/lW8ICm4stfZ4GNQNpzQQ6rhPIB0p3PMZDIgX+6DoMivdNvLRmXAOOpzpIpDg=="],
"@tanstack/router-devtools-core/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@tanstack/router-generator/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@tanstack/router-generator/jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], "@tanstack/router-generator/jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
"@tanstack/router-generator/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "@tanstack/router-generator/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"@tanstack/router-plugin/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@tanstack/router-plugin/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "@tanstack/router-plugin/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"@tanstack/router-utils/diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="], "@tanstack/router-utils/diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="],
"@tanstack/router-utils/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], "@tanstack/router-utils/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
"@tanstack/start-client-core/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@tanstack/start-client-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], "@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
"@tanstack/start-plugin-core/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.40", "", {}, "sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w=="], "@tanstack/start-plugin-core/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.40", "", {}, "sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w=="],
"@tanstack/start-plugin-core/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@tanstack/start-plugin-core/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], "@tanstack/start-plugin-core/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
"@tanstack/start-plugin-core/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "@tanstack/start-plugin-core/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
"@tanstack/start-plugin-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"@tanstack/start-plugin-core/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], "@tanstack/start-plugin-core/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
"@tanstack/start-plugin-core/srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], "@tanstack/start-plugin-core/srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="],
@ -3593,16 +3563,6 @@
"@tanstack/start-plugin-core/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "@tanstack/start-plugin-core/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"@tanstack/start-server-core/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@tanstack/start-server-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"@tanstack/start-storage-context/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@trpc/client/@trpc/server": ["@trpc/server@11.13.4", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-SZmLwi43KSp1D3jp2aPuzqhC644gIy87hgFuD9xWCTwFspH9Pr5DO2/JmpWjCqybTG2fTuKYzxFYLUGjTY5LGg=="],
"@trpc/react-query/@trpc/server": ["@trpc/server@11.13.4", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-SZmLwi43KSp1D3jp2aPuzqhC644gIy87hgFuD9xWCTwFspH9Pr5DO2/JmpWjCqybTG2fTuKYzxFYLUGjTY5LGg=="],
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
@ -3649,6 +3609,8 @@
"common-ui/react-native-pager-view": ["react-native-pager-view@6.9.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-uUT0MMMbNtoSbxe9pRvdJJKEi9snjuJ3fXlZhG8F2vVMOBJVt/AFtqMPUHu9yMflmqOr08PewKzj9EPl/Yj+Gw=="], "common-ui/react-native-pager-view": ["react-native-pager-view@6.9.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-uUT0MMMbNtoSbxe9pRvdJJKEi9snjuJ3fXlZhG8F2vVMOBJVt/AFtqMPUHu9yMflmqOr08PewKzj9EPl/Yj+Gw=="],
"common-ui/zustand": ["zustand@5.0.13", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ=="],
"compressible/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], "compressible/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
"compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
@ -3707,6 +3669,8 @@
"fallback-ui/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "fallback-ui/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"fallback-ui/zustand": ["zustand@5.0.13", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"fbjs/promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="], "fbjs/promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="],
@ -3839,10 +3803,6 @@
"simple-plist/bplist-parser": ["bplist-parser@0.3.1", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA=="], "simple-plist/bplist-parser": ["bplist-parser@0.3.1", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA=="],
"solid-js/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"solid-js/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
@ -3863,6 +3823,8 @@
"tailwindcss/arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], "tailwindcss/arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
"tailwindcss/postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
"tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
"tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
@ -3885,25 +3847,15 @@
"tunnel-rat/zustand": ["zustand@4.5.7", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "optionalPeers": ["@types/react", "immer", "react"] }, "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw=="], "tunnel-rat/zustand": ["zustand@4.5.7", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "optionalPeers": ["@types/react", "immer", "react"] }, "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw=="],
"user-ui/@trpc/client": ["@trpc/client@11.17.0", "", { "peerDependencies": { "@trpc/server": "11.17.0", "typescript": ">=5.7.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-KpJBFrbKTDeVCFv/3ckL1XBBH5Yssn8hethI/rUy7GIpTj+VzjtPjykDqJpzobuVOz+d26cXCSu1t4I6MYI5Zg=="],
"user-ui/@trpc/react-query": ["@trpc/react-query@11.17.0", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.17.0", "@trpc/server": "11.17.0", "react": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-AGcl5YAF8NnhBmyJ6PqJqKb1M5VTGSoNRNqJ3orct4o4epdcg0GWhW+qT9q6gPzs/2ImIwYCdfFpgNGdZ9yLHA=="],
"user-ui/axios": ["axios@1.16.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w=="],
"user-ui/expo-location": ["expo-location@18.1.6", "", { "peerDependencies": { "expo": "*" } }, "sha512-l5dQQ2FYkrBgNzaZN1BvSmdhhcztFOUucu2kEfDBMV4wSIuTIt/CKsho+F3RnAiWgvui1wb1WTTf80E8zq48hA=="], "user-ui/expo-location": ["expo-location@18.1.6", "", { "peerDependencies": { "expo": "*" } }, "sha512-l5dQQ2FYkrBgNzaZN1BvSmdhhcztFOUucu2kEfDBMV4wSIuTIt/CKsho+F3RnAiWgvui1wb1WTTf80E8zq48hA=="],
"user-ui/fuse.js": ["fuse.js@7.3.0", "", {}, "sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w=="], "user-ui/fuse.js": ["fuse.js@7.3.0", "", {}, "sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w=="],
"vite/postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
"vitest/vite": ["vite@8.0.11", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.14", "rolldown": "1.0.0-rc.18", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow=="], "vitest/vite": ["vite@8.0.11", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.14", "rolldown": "1.0.0-rc.18", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow=="],
"web-ui/@tanstack/react-query": ["@tanstack/react-query@5.100.9", "", { "dependencies": { "@tanstack/query-core": "5.100.9" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-Oa44XkaI3kCNN6ME0KByU3xT3SEUNOMfZpHxL6+wFoTm+OeUFYHKdeYVe0aOXlRDm/f15sgLwEt2HDorIdW8+A=="], "web-components/postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
"web-ui/@tanstack/react-router": ["@tanstack/react-router@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.169.2", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-OJM7Kguc7ERnweaNRWsyWgIKcl3z23rD1B4jaxjzd9RGdnzpt2HfrWa9rggbT0Hfzhfo4D2ZmsfoTme035tniQ=="],
"web-ui/@trpc/client": ["@trpc/client@11.17.0", "", { "peerDependencies": { "@trpc/server": "11.17.0", "typescript": ">=5.7.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-KpJBFrbKTDeVCFv/3ckL1XBBH5Yssn8hethI/rUy7GIpTj+VzjtPjykDqJpzobuVOz+d26cXCSu1t4I6MYI5Zg=="],
"web-ui/@trpc/react-query": ["@trpc/react-query@11.17.0", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.17.0", "@trpc/server": "11.17.0", "react": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-AGcl5YAF8NnhBmyJ6PqJqKb1M5VTGSoNRNqJ3orct4o4epdcg0GWhW+qT9q6gPzs/2ImIwYCdfFpgNGdZ9yLHA=="],
"web-ui/@types/node": ["@types/node@22.19.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ=="], "web-ui/@types/node": ["@types/node@22.19.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ=="],
@ -3911,8 +3863,6 @@
"web-ui/@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], "web-ui/@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
"web-ui/axios": ["axios@1.16.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w=="],
"web-ui/fuse.js": ["fuse.js@7.3.0", "", {}, "sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w=="], "web-ui/fuse.js": ["fuse.js@7.3.0", "", {}, "sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w=="],
"web-ui/react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], "web-ui/react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="],
@ -4059,66 +4009,10 @@
"@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], "@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
"@tanstack/react-query-devtools/@tanstack/react-query/@tanstack/query-core": ["@tanstack/query-core@5.100.9", "", {}, "sha512-SJSFw1S8+kQ0+knv/XGfrbocWoAlT7vDKsSImtLx3ZPQmEcR46hkDjLSvynSy25N8Ms4tIEini1FuBd5k7IscQ=="],
"@tanstack/react-start-client/@tanstack/react-router/@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="],
"@tanstack/react-start-client/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"@tanstack/react-start-client/@tanstack/router-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"@tanstack/react-start-client/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"@tanstack/react-start-rsc/@tanstack/react-router/@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="],
"@tanstack/react-start-rsc/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"@tanstack/react-start-rsc/@tanstack/router-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"@tanstack/react-start-rsc/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"@tanstack/react-start-server/@tanstack/react-router/@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="],
"@tanstack/react-start-server/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"@tanstack/react-start-server/@tanstack/router-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"@tanstack/react-start-server/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"@tanstack/react-start/@tanstack/react-router/@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="],
"@tanstack/react-start/@tanstack/react-router/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"@tanstack/router-devtools-core/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"@tanstack/router-devtools-core/@tanstack/router-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"@tanstack/router-devtools-core/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"@tanstack/router-devtools/@tanstack/react-router-devtools/@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.166.9", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "tiny-invariant": "^1.3.3" }, "peerDependencies": { "@tanstack/router-core": "^1.167.2", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-PNlA7GmOUX9wY7LUG709Pk3Lg33dfHBztQwzjzrOiOsuf4ggp2R6bwarF8nYGNjG79z/MaB5PN+5yvkCVk8jGw=="], "@tanstack/router-devtools/@tanstack/react-router-devtools/@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.166.9", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "tiny-invariant": "^1.3.3" }, "peerDependencies": { "@tanstack/router-core": "^1.167.2", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-PNlA7GmOUX9wY7LUG709Pk3Lg33dfHBztQwzjzrOiOsuf4ggp2R6bwarF8nYGNjG79z/MaB5PN+5yvkCVk8jGw=="],
"@tanstack/router-generator/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"@tanstack/router-generator/@tanstack/router-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"@tanstack/router-generator/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"@tanstack/router-plugin/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"@tanstack/router-plugin/@tanstack/router-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"@tanstack/router-plugin/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"@tanstack/router-utils/tinyglobby/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "@tanstack/router-utils/tinyglobby/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
"@tanstack/start-client-core/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"@tanstack/start-client-core/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"@tanstack/start-plugin-core/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"@tanstack/start-plugin-core/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"@tanstack/start-plugin-core/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], "@tanstack/start-plugin-core/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
"@tanstack/start-plugin-core/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], "@tanstack/start-plugin-core/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
@ -4139,16 +4033,6 @@
"@tanstack/start-plugin-core/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], "@tanstack/start-plugin-core/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
"@tanstack/start-server-core/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"@tanstack/start-server-core/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"@tanstack/start-storage-context/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"@tanstack/start-storage-context/@tanstack/router-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"@tanstack/start-storage-context/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
"@unrs/resolver-binding-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core": ["@emnapi/core@1.9.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w=="], "@unrs/resolver-binding-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core": ["@emnapi/core@1.9.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w=="],
@ -4157,8 +4041,6 @@
"@vitejs/plugin-react/vite/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "@vitejs/plugin-react/vite/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
"@vitejs/plugin-react/vite/postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="],
"@vitejs/plugin-react/vite/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], "@vitejs/plugin-react/vite/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
@ -4319,36 +4201,18 @@
"tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.4", "", { "os": "win32", "cpu": "x64" }, "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg=="], "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.4", "", { "os": "win32", "cpu": "x64" }, "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg=="],
"user-ui/axios/follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
"user-ui/axios/proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
"vitest/vite/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], "vitest/vite/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
"vitest/vite/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "vitest/vite/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
"vitest/vite/postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="],
"vitest/vite/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], "vitest/vite/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
"web-ui/@tanstack/react-query/@tanstack/query-core": ["@tanstack/query-core@5.100.9", "", {}, "sha512-SJSFw1S8+kQ0+knv/XGfrbocWoAlT7vDKsSImtLx3ZPQmEcR46hkDjLSvynSy25N8Ms4tIEini1FuBd5k7IscQ=="],
"web-ui/@tanstack/react-router/@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="],
"web-ui/@tanstack/react-router/@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="],
"web-ui/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "web-ui/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"web-ui/axios/follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
"web-ui/axios/proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
"web-ui/vite/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], "web-ui/vite/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
"web-ui/vite/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "web-ui/vite/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
"web-ui/vite/postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="],
"web-ui/vite/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], "web-ui/vite/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
"web-ui/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="], "web-ui/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="],
@ -4541,20 +4405,6 @@
"@istanbuljs/load-nyc-config/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
"@tanstack/react-start-client/@tanstack/react-router/@tanstack/react-store/@tanstack/store": ["@tanstack/store@0.9.3", "", {}, "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw=="],
"@tanstack/react-start-rsc/@tanstack/react-router/@tanstack/react-store/@tanstack/store": ["@tanstack/store@0.9.3", "", {}, "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw=="],
"@tanstack/react-start-server/@tanstack/react-router/@tanstack/react-store/@tanstack/store": ["@tanstack/store@0.9.3", "", {}, "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw=="],
"@tanstack/react-start/@tanstack/react-router/@tanstack/react-store/@tanstack/store": ["@tanstack/store@0.9.3", "", {}, "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw=="],
"@tanstack/react-start/@tanstack/react-router/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"@tanstack/react-start/@tanstack/react-router/@tanstack/router-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"@tanstack/react-start/@tanstack/react-router/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"@unrs/resolver-binding-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="], "@unrs/resolver-binding-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="],
@ -4625,14 +4475,6 @@
"vitest/vite/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], "vitest/vite/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
"web-ui/@tanstack/react-router/@tanstack/react-store/@tanstack/store": ["@tanstack/store@0.9.3", "", {}, "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw=="],
"web-ui/@tanstack/react-router/@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
"web-ui/@tanstack/react-router/@tanstack/router-core/seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"web-ui/@tanstack/react-router/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"web-ui/vite/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], "web-ui/vite/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
"web-ui/vite/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], "web-ui/vite/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],

View file

@ -470,3 +470,137 @@ EDITED:
- Deleted: apps/backend/src/lib/redis-client.ts, apps/backend/src/lib/redisKeyGetters.ts, apps/backend/src/middleware/auth.ts (middleware/ dir still has auth.middleware.ts + staff-auth.ts). - Deleted: apps/backend/src/lib/redis-client.ts, apps/backend/src/lib/redisKeyGetters.ts, apps/backend/src/middleware/auth.ts (middleware/ dir still has auth.middleware.ts + staff-auth.ts).
- Removed CacheFilename type from packages/shared/index.ts (CACHE_FILENAMES const kept — used by cloud_cache.ts + web-ui/user-ui hooks). - Removed CacheFilename type from packages/shared/index.ts (CACHE_FILENAMES const kept — used by cloud_cache.ts + web-ui/user-ui hooks).
- Verified: zero remaining references (excluding comments); backend tsc --noEmit = 11 errors, same pre-existing baseline. - Verified: zero remaining references (excluding comments); backend tsc --noEmit = 11 errors, same pre-existing baseline.
[2026-09-02 21:45:00] CREATED analysis scripts (new .js files, read-only analysis): scripts/dead-code/user-ui-files.js, user-ui-symbols.js, user-ui-suspects.js, user-ui-store-actions.js, user-ui-store-final.js, user-ui-store-precise.js
[2026-09-02 22:56:40] EXECUTE user_ui_glm.md cleanup: remove dead code, KEEP register.tsx, DO NOT touch npm dependencies.
--- FILE DELETIONS ---
- apps/user-ui/app/(drawer)/(tabs)/me/about/_layout.tsx (unreachable route, zero navigations)
- apps/user-ui/app/(drawer)/(tabs)/me/about/index.tsx (unreachable route)
KEPT: app/(auth)/register.tsx (user decision — keep for future sign-up wiring)
- apps/user-ui/constants/Colors.ts (never imported; constants/ dir becomes empty)
- apps/user-ui/components/BannerCarousel.tsx (never imported)
- apps/user-ui/src/components/google-sign-in.tsx (never imported; login uses common-ui's copy)
- apps/user-ui/app/api/auth/authorize+api.ts (unreferenced Google OAuth endpoints; ⚠️ no caller in repo —
- apps/user-ui/app/api/auth/callback+api.ts restore from git if an external Google redirect URI was configured)
- apps/user-ui/app/api/auth/token+api.ts
- UNUSED ASSETS (zero references in code/app.json/eas.json):
assets/images/adaptive-icon.png, farm2door-logo.png, freshyo-logoo.png, freshyo-logoo.svg,
logo_mini.jpg, partial-react-logo.png, react-logo.png, react-logo@2x.png, react-logo@3x.png,
splash-icon.png, symbuyote.png
(assets/images/logo.png, icon.png, favicon.png, freshyo-logo.png, fonts/SpaceMono-Regular.ttf KEPT — referenced)
--- EDITS ---
=== apps/user-ui/src/hooks/prominent-api-hooks.ts ===
- remove dead chain: useBanners() hook + BannersResponse type (only consumer was deleted BannerCarousel)
(CACHE_FILENAMES import stays — used by other cache hooks in the file)
=== apps/user-ui/hooks/useJWT.ts ===
- removed saveRoles, getRoles, deleteRoles (never called) + ROLES_KEY const (only used by the dead trio)
(AUTH_TOKEN_KEY, USER_ID_KEY, saveUserId, getUserId, saveAuthToken, getAuthToken, deleteAuthToken kept — live)
=== apps/user-ui/services/toaster.tsx ===
- removed InfoToast, ErrorToast, SuccessToast (never imported; NotificationToast + default Toast are live)
=== apps/user-ui/src/store/centralProductStore.ts ===
- removed clearProducts (interface field + implementation; nothing ever clears the product store)
=== apps/user-ui/src/store/addressStore.ts ===
- removed clearSelectedAddress (interface field + implementation)
=== apps/user-ui/src/store/centralSlotStore.ts ===
- removed clearSlotsData (interface field + implementation)
=== apps/user-ui/src/store/quickDeliveryStore.ts ===
- removed setDrawerHidden (never called; isDrawerHidden state + readers kept — flag is permanently false today)
=== apps/user-ui/components/PaymentAndOrderComponent.tsx ===
- removed commented createRazorpayOrderMutation block (~lines 97-104)
- removed commented initiateRazorpayPayment block (~lines 170-210)
(live COD/verify-payment flow untouched; react-native-razorpay dependency KEPT per instruction)
=== apps/user-ui/app/(drawer)/(tabs)/me/my-orders/index.tsx ===
- removed commented initiateRazorpayPayment block (~lines 564-585)
(live handleRetryPayment + createRazorpayOrderMutation untouched)
=== apps/user-ui/app/_layout.tsx ===
- removed line 40: console.log('from layout')
- line 11 import: removed unused Dimensions → import { Appearance, StatusBar, View } from "react-native";
NOT TOUCHED (per instruction): all npm dependencies in package.json (incl. react-native-razorpay, expo-image-picker, etc.)
NOT TOUCHED (left as-is): scripts/reset-project.js (template helper wired to npm script — user call), dist/ build artifacts
[2026-09-02 23:02:51] COMPLETED user_ui_glm.md cleanup (register.tsx KEPT, npm dependencies NOT touched).
Deleted: me/about route (2 files + dir), constants/Colors.ts (+ empty constants/ dir), components/BannerCarousel.tsx, src/components/google-sign-in.tsx, app/api/auth/{authorize,callback,token}+api.ts (+ empty dirs), 11 unused assets.
Edited: prominent-api-hooks.ts (useBanners + BannersResponse + BannersApiType import removed), useJWT.ts (saveRoles/getRoles/deleteRoles/ROLES_KEY removed), toaster.tsx (InfoToast/ErrorToast/SuccessToast removed), 4 stores (clearProducts, clearSelectedAddress, clearSlotsData, setDrawerHidden removed), PaymentAndOrderComponent.tsx + my-orders/index.tsx (commented Razorpay blocks removed), _layout.tsx (console.log + unused Dimensions import removed).
Verification: zero leftover references; tsc --noEmit total unchanged at 106 (all pre-existing, in ../backend files via @backend types; 0 errors in user-ui's own files).
[2026-09-02 23:45:00] EXECUTE admin_ui_dpsk.md cleanup: delete dead code; KEEP §4 (single-source routes + dead affordances); DO NOT touch npm dependencies.
PLANNED FILE DELETIONS (verified zero live references via grep across apps/admin-ui + packages):
- app/(drawer)/dashboard/coupons/reserved-coupons/index.tsx (orphaned route; feature duplicated inline as ReservedTab in coupons/index.tsx)
- components/context/auth-context.tsx (100% commented-out legacy auth; no live exports)
- components/context/roles-context.tsx (imports non-export AuthContext → broken; useRoles/useIsAdmin zero importers)
- components/dashboard-header.tsx (zero importers; replaced by drawer header)
- components/TabNavigation.tsx, components/day-account-view.tsx, components/HorizontalImageScroller.tsx (zero importers)
- components/AddressPlaceForm.tsx, components/AddressZoneForm.tsx, components/app-container.tsx (zero importers)
- components/ui/IconSymbol.ios.tsx, IconSymbol.tsx, TabBarBackground.ios.tsx, TabBarBackground.tsx (expo-template tab files; zero importers)
- components/UserIncidentDialog.tsx (zero importers; live UserIncidentsView.tsx holds private duplicate)
- components/date-time-picker.tsx (zero importers; duplicate of common-ui's live DateTimePickerMod)
- src/api-hooks/banner.api.ts (zero importers)
- hooks/useHideDrawerHeader.ts, hooks/useCurrentUserId.ts, hooks/usePhonepeSdk.ts (zero importers)
- hooks/useThemeColor.ts, hooks/useColorScheme.ts, hooks/useColorScheme.web.ts, constants/Colors.ts (template trio; sole consumer = deleted dashboard-header)
- utils/getCurrentUserId.ts (zero live importers)
- services/axios-admin-ui.ts (zero importers)
- services/notif-service/notif-checker.tsx, notif-context.tsx, notif-register.ts (never mounted in app/_layout; notif-checker imports nonexistent @/api-hooks/user.api)
- scripts/reset-project.js + e2e/jest.config.js + e2e/starter.test.js + .detoxrc.js (expo-template leftovers; no detox dep/script)
PLANNED EDITS (with false-positive corrections verified by reading live callers):
=== apps/admin-ui/app/(drawer)/dashboard/index.tsx ===
- remove unused import: LinearGradient (theme import KEPT — used by menu items; only the LinearGradient JSX block is commented out)
=== apps/admin-ui/app/(drawer)/dashboard/send-notifications/index.tsx ===
- remove commented-out ImageUploader UI block; remove now-unused ImageUploader/usePickImage imports, handleImagePick/handleRemoveImage handlers, selectedImage/displayImage state, and their reset lines in sendNotification.onSuccess
(image-upload feature is commented out/unreachable; useUploadToObjectStorage.uploadSingle KEPT — still used in handleSend)
=== apps/admin-ui/app/(drawer)/dashboard/manage-orders/delivery-sequences/index.tsx ===
- remove commented-out hint-banner JSX block inside the DraggableFlatList
=== apps/admin-ui/hooks/useJWT.ts ===
- prune to live exports only: saveJWT, getJWT, deleteJWT (+ JWT_KEY const). Remove: saveUserId, getUserId, saveRoles, getRoles, deleteRoles, ROLES_KEY, USER_ID_KEY
=== apps/admin-ui/src/components/CouponForm.tsx ===
- remove console.log('Toggling user:', ...) debug line (keep setFieldValue)
=== apps/admin-ui/components/StoreForm.tsx ===
- remove StoreFormRef interface + forwardRef wrapper + unused ref param + displayName (ref never attached by stores/add.tsx or stores/edit.tsx)
- KEPT: ProductForm's ProductFormRef/clearImages (genuinely used by products/edit.tsx via productFormRef.current?.clearImages())
- KEPT: SlotForm's dateTestID/timeTestID props (common-ui DateTimePickerMod accepts them — verified in packages/ui/src/components/date-time-picker.tsx)
- KEPT: OrderOptionsMenu onWhatsApp/onDial props and no-op callers (§4 dead-affordance zone — not deleting)
NOT TOUCHED (per instruction):
- §4: rebalance-orders route, slots/slot-details route, customize-app/product-tags home-grid entries, vendor-snippets "View Slot" no-op button, orders/index packaged-checkbox no-op onPress
- All npm dependencies in package.json (incl. react-native-phonepe-pg, expo-notifications, material-top-tabs, toaster dep, buffer, jwt-decode)
- components/SnippetMenu.tsx SuccessToast call + services/toaster.tsx (product decision — no <Toast/> host mounted)
- user-ui counterparts (LIVE there; e.g. user-ui's useColorScheme/Colors/notif-service are wired and in use)
[2026-09-02 23:58:00] COMPLETED admin_ui_dpsk.md cleanup.
Deleted 34 dead files + emptied dirs:
- Route: app/(drawer)/dashboard/coupons/reserved-coupons/index.tsx (+ emptied dir)
- Components: context/auth-context.tsx, context/roles-context.tsx, dashboard-header.tsx, TabNavigation.tsx, day-account-view.tsx, HorizontalImageScroller.tsx, AddressPlaceForm.tsx, AddressZoneForm.tsx, app-container.tsx, UserIncidentDialog.tsx, date-time-picker.tsx, ui/{IconSymbol,IconSymbol.ios,TabBarBackground,TabBarBackground.ios}.tsx (+ emptied components/ui dir)
- src/api-hooks/banner.api.ts (+ emptied dir)
- Hooks: useHideDrawerHeader.ts, useCurrentUserId.ts, usePhonepeSdk.ts, useThemeColor.ts, useColorScheme.ts, useColorScheme.web.ts
- constants/Colors.ts (+ emptied dir), utils/getCurrentUserId.ts, services/axios-admin-ui.ts, services/notif-service/{notif-checker,notif-context,notif-register} (+ emptied dir)
- scripts/reset-project.js (+ emptied dir), e2e/{jest.config.js,starter.test.js} (+ emptied dir), .detoxrc.js
- components/context/ dir kept (staff-auth-context.tsx is live)
Edited:
- app/(drawer)/dashboard/index.tsx: removed unused LinearGradient import (theme kept — used by menu items; commented gradient JSX block left as comment)
- app/(drawer)/dashboard/send-notifications/index.tsx: removed commented-out image-upload UI + dead image state (selectedImage/displayImage), handleImagePick/handleRemoveImage handlers, ImageUploader/usePickImage/useUploadToObjectStorage imports, and image upload path in handleSend (UI is disabled; uploadSingle had no reachable caller left)
- app/(drawer)/dashboard/manage-orders/delivery-sequences/index.tsx: removed commented-out hint-banner JSX block
- hooks/useJWT.ts: pruned to saveJWT/getJWT/deleteJWT + JWT_KEY (removed saveUserId/getUserId/saveRoles/getRoles/deleteRoles/ROLES_KEY/USER_ID_KEY)
- src/components/CouponForm.tsx: removed debug console.log in toggleUserSelection
- components/StoreForm.tsx: removed unused StoreFormRef interface + forwardRef wrapper + ref param + displayName (now plain function component; StoreFormData export kept)
- Regenerated .expo/types/router.d.ts via brief expo start (removed stale reserved-coupons route types); dev server killed
KEPT (per instruction / verification): §4 routes & affordances (rebalance-orders, slot-details, customize-app/product-tags, vendor-snippets "View Slot" no-op, orders packaged-checkbox no-op); ProductForm ProductFormRef/clearImages (USED by products/edit.tsx); SlotForm dateTestID/timeTestID (common-ui DateTimePickerMod accepts them); OrderOptionsMenu onWhatsApp/onDial props; SnippetMenu SuccessToast + services/toaster.tsx (no Toast host — product decision); all npm dependencies untouched; user-ui counterparts untouched (live there).
Verification:
- grep across apps/admin-ui: zero remaining references to any deleted file/symbol (only generated .expo cache regenerated clean).
- tsc --noEmit: no errors in any file I edited. Remaining 19 admin-ui-local errors + backend errors are pre-existing (in untouched files: complaints, coupons/edit, customize-app, products, user-management, delivery-sequences lines 343/398 implicit-any, ProductGroupForm, ProductsSelector, ProductForm) — verified none are in modified regions.

View file

@ -0,0 +1,48 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const ROOT = '/Users/mohammedshafiuddin/WebDev/freshyo'
const APP = 'apps/user-ui'
// all user-ui source files
const files = execSync(
`find ${APP} -type f \\( -name '*.ts' -o -name '*.tsx' \\) -not -path '*/node_modules/*' -not -path '*/dist/*' -not -path '*/.expo/*' -not -path '*/.turbo/*'`,
{ cwd: ROOT }
).toString().trim().split('\n')
// all ts/tsx content across the whole repo that could import these (user-ui + repo scripts)
const scannerFiles = execSync(
`find apps/user-ui scripts packages/migrator -type f \\( -name '*.ts' -o -name '*.tsx' -o -name '*.js' \\) -not -path '*/node_modules/*' -not -path '*/dist/*' -not -path '*/.expo/*' -not -path '*/.turbo/*'`,
{ cwd: ROOT }
).toString().trim().split('\n')
const contents = new Map()
for (const f of scannerFiles) {
try { contents.set(f, fs.readFileSync(path.join(ROOT, f), 'utf8')) } catch {}
}
// route files are entry points (expo-router) — exclude from "never imported" check
const isRoute = (f) => f.startsWith(`${APP}/app/`) && !f.includes('+api')
const isConfig = (f) => /metro\.config|eslint\.config|tsconfig|expo-env/.test(f)
const neverImported = []
for (const f of files) {
if (isRoute(f) || isConfig(f)) continue
const base = path.basename(f).replace(/\.(ts|tsx)$/, '')
// special web variants
const candidates = [base]
if (base.includes('.')) candidates.push(base) // useColorScheme.web
const baseNoPlatform = base.replace(/\.web$/, '')
const re = new RegExp(`from ['"][^'"]*${baseNoPlatform.replace(/\./g, '\\.')}['"]|require\\(['"][^'"]*${baseNoPlatform.replace(/\./g, '\\.')}['"]\\)`)
const importers = []
for (const [of, c] of contents) {
if (of === f) continue
if (re.test(c)) importers.push(of)
}
if (importers.length === 0) neverImported.push(f)
}
console.log('=== FILES NEVER IMPORTED (excluding expo-router entry points) ===')
neverImported.forEach((f) => console.log(f))
console.log('count:', neverImported.length)

View file

@ -0,0 +1,34 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const ROOT = process.cwd()
const storeFiles = execSync(
"find apps/user-ui/src/store apps/user-ui/components/stores -type f -name '*.ts'"
)
.toString()
.trim()
.split('\n')
// collect all non-store user-ui code
const consumerFiles = execSync(
"find apps/user-ui -type f \\( -name '*.ts' -o -name '*.tsx' \\) -not -path '*/node_modules/*' -not -path '*/dist/*' -not -path '*/.expo/*' -not -path '*/.turbo/*'"
)
.toString()
.trim()
.split('\n')
.filter((f) => !f.includes('/store/'))
const consumerCode = consumerFiles.map((f) => fs.readFileSync(path.join(ROOT, f), 'utf8')).join('\n')
for (const f of storeFiles) {
const code = fs.readFileSync(path.join(ROOT, f), 'utf8')
// keys inside the create<...>((set) => ({ ... })) object: "name:" at 2-space indent
const keys = [...code.matchAll(/^ {2}(\w+):/gm)].map((m) => m[1])
const dead = []
for (const k of new Set(keys)) {
const re = new RegExp(`\\.${k}\\b`)
if (!re.test(consumerCode)) dead.push(k)
}
console.log(path.basename(f), '→ unused actions/state:', dead.length ? dead.join(', ') : 'none')
}

View file

@ -0,0 +1,36 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const ROOT = process.cwd()
const storeFiles = execSync(
"find apps/user-ui/src/store apps/user-ui/components/stores -type f -name '*.ts'"
)
.toString()
.trim()
.split('\n')
const otherFiles = execSync(
"find apps/user-ui -type f \\( -name '*.ts' -o -name '*.tsx' \\) -not -path '*/node_modules/*' -not -path '*/dist/*' -not -path '*/.expo/*' -not -path '*/.turbo/*'"
)
.toString()
.trim()
.split('\n')
.filter((f) => !f.includes('/store/'))
for (const f of storeFiles) {
const code = fs.readFileSync(path.join(ROOT, f), 'utf8')
const keys = [...new Set([...code.matchAll(/^ {2}(\w+):/gm)].map((m) => m[1]))]
const dead = []
for (const k of keys) {
const re = new RegExp(`\\b${k}\\b`, 'g')
const ownCount = (code.match(re) || []).length // def + internal uses
let outside = 0
for (const of of otherFiles) {
outside += (fs.readFileSync(path.join(ROOT, of), 'utf8').match(re) || []).length
}
// ownCount === 1 means only the definition line; outside === 0 means nobody else
if (ownCount <= 1 && outside === 0) dead.push(k)
}
console.log(path.basename(f), '→ TRULY unused:', dead.length ? dead.join(', ') : 'none')
}

View file

@ -0,0 +1,33 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const ROOT = process.cwd()
const storeFiles = execSync(
"find apps/user-ui/src/store apps/user-ui/components/stores -type f -name '*.ts'"
)
.toString()
.trim()
.split('\n')
const otherFiles = execSync(
"find apps/user-ui -type f \\( -name '*.ts' -o -name '*.tsx' \\) -not -path '*/node_modules/*' -not -path '*/dist/*' -not -path '*/.expo/*' -not -path '*/.turbo/*'"
)
.toString()
.trim()
.split('\n')
.filter((f) => !f.includes('/store/'))
for (const f of storeFiles) {
const lines = fs.readFileSync(path.join(ROOT, f), 'utf8').split('\n')
const keys = [...new Set(lines.map((l) => (l.match(/^ {2}(\w+):/) || [])[1]).filter(Boolean))]
const dead = []
for (const k of keys) {
const re = new RegExp(`\\b${k}\\b`)
// own-file usage: lines where the key appears but NOT as a "key:" declaration
const internalUse = lines.some((l) => re.test(l) && !l.trimStart().startsWith(k + ':'))
const outside = otherFiles.some((of) => re.test(fs.readFileSync(path.join(ROOT, of), 'utf8')))
if (!internalUse && !outside) dead.push(k)
}
console.log(path.basename(f), '→ DEAD members:', dead.length ? dead.join(', ') : 'none')
}

View file

@ -0,0 +1,54 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const ROOT = '/Users/mohammedshafiuddin/WebDev/freshyo'
const APP = 'apps/user-ui'
const files = execSync(
`find ${APP} -type f \\( -name '*.ts' -o -name '*.tsx' \\) -not -path '*/node_modules/*' -not -path '*/dist/*' -not -path '*/.expo/*' -not -path '*/.turbo/*'`,
{ cwd: ROOT }
).toString().trim().split('\n')
const codes = new Map()
for (const f of files) codes.set(f, fs.readFileSync(path.join(ROOT, f), 'utf8'))
const suspects = [
'useJWT', 'toaster', 'notif-checker', 'notif-register', 'notif-context',
'useColorScheme', 'useUploadToObjectStore', 'useProductSlotIdentifier',
'getCurrentUserId', 'queryClient', 'string-manipulators',
'AddressForm', 'AddToCartDialog', 'CentralStoreInitializer', 'google-sign-in',
'LocationAttacher', 'FirstUserWrapper', 'FlashDeliveryNote', 'HealthTestWrapper',
'LocationTestWrapper', 'NextOrderGlimpse', 'OrderMenu', 'QuickDeliveryAddressSelector',
'registration-form', 'TabLayoutWrapper', 'TestingPhaseNote', 'UpdateChecker',
'WebViewWrapper', 'CheckoutAddressSelector', 'ComplaintForm', 'ProductDetail',
'ProductCard', 'BackHandler', 'floating-cart-bar', 'cart-page', 'checkout-page',
'PaymentAndOrderComponent', 'SlotSpecificView', 'cart-query-hooks', 'useAuthenticatedRoute',
]
for (const name of suspects) {
const re = new RegExp(`from ['"][^'"]*/${name}['"]`)
const importers = []
for (const [of, c] of codes) {
if (re.test(c)) importers.push(of.replace(APP + '/', ''))
}
console.log(`${name}: ${importers.length === 0 ? '*** NEVER IMPORTED ***' : importers.join(', ')}`)
}
// store hooks usage
console.log('\n=== STORE HOOKS ===')
const storeFiles = files.filter((f) => f.includes('/store/'))
for (const sf of storeFiles) {
const code = codes.get(sf)
const m = code.match(/export\s+const\s+(use\w+)/g) || []
const hooks = m.map((x) => x.replace('export const ', ''))
for (const h of hooks) {
let refs = 0
const filesUsing = []
for (const [of, oc] of codes) {
if (of === sf) continue
if (new RegExp(`\\b${h}\\b`).test(oc)) { refs++; filesUsing.push(of.replace(APP + '/', '')) }
}
console.log(`${h} (${path.basename(sf)}): ${refs === 0 ? '*** UNUSED ***' : refs + ' refs'}`)
}
}

View file

@ -0,0 +1,62 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const ROOT = '/Users/mohammedshafiuddin/WebDev/freshyo'
const APP = 'apps/user-ui'
const files = execSync(
`find ${APP} -type f \\( -name '*.ts' -o -name '*.tsx' \\) -not -path '*/node_modules/*' -not -path '*/dist/*' -not -path '*/.expo/*' -not -path '*/.turbo/*'`,
{ cwd: ROOT }
).toString().trim().split('\n')
const codes = new Map()
for (const f of files) codes.set(f, fs.readFileSync(path.join(ROOT, f), 'utf8'))
function countWord(code, sym) {
const re = new RegExp(`\\b${sym.replace(/\$/g, '\\$')}\\b`, 'g')
return (code.match(re) || []).length
}
function exportedNames(code) {
const names = new Set()
let m
const pats = [
/export\s+(?:default\s+)?(?:async\s+)?function\s*\*?\s*(\w+)/g,
/export\s+(?:const|let|var)\s+(\w+)/g,
/export\s+(?:type|interface|class|enum)\s+(\w+)/g,
]
for (const p of pats) while ((m = p.exec(code))) names.add(m[1])
const br = /export\s*{([^}]*)}/g
while ((m = br.exec(code))) {
for (let part of m[1].split(',')) {
part = part.replace(/\/\/[^\n]*/g, '').trim().replace(/^type\s+/, '')
if (!part) continue
const asM = part.match(/\bas\s+(\w+)$/)
const nm = asM ? asM[1] : part.split(/\s/)[0]
if (/^[\w$]+$/.test(nm)) names.add(nm)
}
}
if (/export\s+default\s+function/.test(code) || /export\s+default\s+\w+/.test(code)) names.add('__default__')
return [...names]
}
console.log('=== UNUSED EXPORTS per file (symbol not referenced in any OTHER user-ui file) ===')
for (const [f, code] of codes) {
if (f.startsWith(`${APP}/app/`)) continue // routes
const names = exportedNames(code)
const unused = []
for (const sym of names) {
if (sym === '__default__') continue
let refs = 0
for (const [of, oc] of codes) {
if (of === f) continue
refs += countWord(oc, sym)
}
if (refs === 0) unused.push(sym)
}
if (unused.length) {
console.log(`\n${f.replace(APP + '/', '')}`)
console.log(' ' + unused.join(', '))
}
}