diff --git a/apps/admin-ui/src/components/ProductForm.tsx b/apps/admin-ui/src/components/ProductForm.tsx index b28018c..99b315d 100644 --- a/apps/admin-ui/src/components/ProductForm.tsx +++ b/apps/admin-ui/src/components/ProductForm.tsx @@ -5,11 +5,10 @@ import * as Yup from 'yup' import { MyTextInput, BottomDropdown, MyText, tw, ImageUploaderNeo, ImageUploaderNeoItem, ImageUploaderNeoPayload, Checkbox, InfoDialog } from 'common-ui' import MaterialIcons from '@expo/vector-icons/MaterialIcons' import { trpc } from '../trpc-client' +import type { SkuFeatureLike } from '@packages/shared' -interface Attribute { - featureName: string | null - featureValue: string -} +// Feature input shape — single source: shared SkuFeatureLike +type Attribute = SkuFeatureLike interface Variant { id?: number diff --git a/apps/backend/src/lib/const-keys.ts b/apps/backend/src/lib/const-keys.ts index 0f710db..3f66ce3 100644 --- a/apps/backend/src/lib/const-keys.ts +++ b/apps/backend/src/lib/const-keys.ts @@ -1,3 +1,5 @@ +import type { ConstValueType } from '@packages/shared' + export const CONST_KEYS = { minRegularOrderValue: 'minRegularOrderValue', freeDeliveryThreshold: 'freeDeliveryThreshold', @@ -52,8 +54,6 @@ export type ConstKey = (typeof CONST_KEYS)[keyof typeof CONST_KEYS]; export const CONST_KEYS_ARRAY = Object.values(CONST_KEYS) as ConstKey[]; -export type ConstValueType = 'string' | 'boolean' | 'number' - export const CONST_TYPES: Record = { minRegularOrderValue: 'number', freeDeliveryThreshold: 'number', diff --git a/apps/backend/src/stores/banner-store.ts b/apps/backend/src/stores/banner-store.ts index 5a5ed7a..1c0524c 100644 --- a/apps/backend/src/stores/banner-store.ts +++ b/apps/backend/src/stores/banner-store.ts @@ -1,22 +1,12 @@ // import redisClient from '@/src/lib/redis-client' +import type { BannerData } from '@packages/shared' import { getAllBannersForCache, getBanners, getBannerById as getBannerByIdFromDb, - type BannerData, } from '@/src/dbService' import { scaffoldAssetUrl } from '@/src/lib/s3-client' -// Banner Type (matches getBanners return) -interface Banner { - id: number - name: string - imageUrl: string | null - serialNum: number | null - skuIds: number[] | null - createdAt: Date -} - export async function initializeBannerStore(): Promise { try { console.log('Initializing banner store in Redis...') @@ -59,7 +49,7 @@ export async function initializeBannerStore(): Promise { } } -export async function getBannerById(id: number): Promise { +export async function getBannerById(id: number): Promise { try { // const key = `banner:${id}` // const data = await redisClient.get(key) @@ -87,7 +77,7 @@ export async function getBannerById(id: number): Promise { } } -export async function getAllBanners(): Promise { +export async function getAllBanners(): Promise { try { // Get all keys matching the pattern "banner:*" // const keys = await redisClient.KEYS('banner:*') diff --git a/apps/backend/src/trpc/apis/user-apis/apis/order.ts b/apps/backend/src/trpc/apis/user-apis/apis/order.ts index 5e2874b..6520ebb 100644 --- a/apps/backend/src/trpc/apis/user-apis/apis/order.ts +++ b/apps/backend/src/trpc/apis/user-apis/apis/order.ts @@ -24,6 +24,7 @@ import { } from "@/src/dbService"; import { scaffoldAssetUrl } from "@/src/lib/s3-client"; import { ApiError } from "@/src/lib/api-error"; +import type { DeliveryStatus, OrderStatus } from "@packages/shared"; import { sendOrderPlacedNotification, sendOrderCancelledNotification, @@ -334,9 +335,6 @@ export const orderRouter = router({ const status = order.orderStatus[0]; const refund = order.refunds[0]; - type DeliveryStatus = "cancelled" | "success" | "pending" | "packaged"; - type OrderStatus = "cancelled" | "success"; - let deliveryStatus: DeliveryStatus; let orderStatus: OrderStatus; @@ -474,9 +472,6 @@ export const orderRouter = router({ const status = order.orderStatus[0]; const refund = order.refunds[0]; - type DeliveryStatus = "cancelled" | "success" | "pending" | "packaged"; - type OrderStatus = "cancelled" | "success"; - let deliveryStatus: DeliveryStatus; let orderStatusResult: OrderStatus; diff --git a/apps/fallback-ui/src/components/3d/Characters.tsx b/apps/fallback-ui/src/components/3d/Characters.tsx index 0b919a1..9d078e6 100644 --- a/apps/fallback-ui/src/components/3d/Characters.tsx +++ b/apps/fallback-ui/src/components/3d/Characters.tsx @@ -1,6 +1,7 @@ import { useRef, useEffect, useMemo } from 'react'; import { useFrame } from '@react-three/fiber'; import * as THREE from 'three'; +import type { CelebrationProps } from '@packages/shared'; interface CharacterProps { color: string; @@ -758,11 +759,7 @@ function Character({ color, isCelebrating, delay = 0, type, hairColor = '#2D1B0E ); } -interface CharactersProps { - isCelebrating: boolean; -} - -export default function Characters({ isCelebrating }: CharactersProps) { +export default function Characters({ isCelebrating }: CelebrationProps) { return ( {/* Character 1 - Cheerer - Left side */} diff --git a/apps/fallback-ui/src/components/3d/Scene.tsx b/apps/fallback-ui/src/components/3d/Scene.tsx index 180a8ef..72f42e0 100644 --- a/apps/fallback-ui/src/components/3d/Scene.tsx +++ b/apps/fallback-ui/src/components/3d/Scene.tsx @@ -2,6 +2,7 @@ import { useRef, useEffect } from 'react'; import { Canvas, useFrame, useThree } from '@react-three/fiber'; import { OrbitControls, Stars, Float } from '@react-three/drei'; import * as THREE from 'three'; +import type { CelebrationProps } from '@packages/shared'; import Characters from './Characters'; // Warm gradient background @@ -136,11 +137,7 @@ function CameraController({ isCelebrating }: { isCelebrating: boolean }) { return null; } -interface SceneProps { - isCelebrating: boolean; -} - -export default function Scene({ isCelebrating }: SceneProps) { +export default function Scene({ isCelebrating }: CelebrationProps) { return (
TAG_COLORS[id % TAG_COLORS.length]; -interface RenderStoreProps { - item: any; -} - -const RenderStore = memo(({ item }: RenderStoreProps) => { +const RenderStore = memo(({ item }: ListItemProps) => { const router = useRouter(); const { setNavigatedFromHome, setSelectedStoreId } = useNavigationStore(); @@ -343,11 +340,7 @@ const ExploreProductItem = memo(({ item, onPress }: ProductItemProps) => { ); }); -interface SlotItemProps { - item: any; -} - -const SlotItem = memo(({ item }: SlotItemProps) => ); +const SlotItem = memo(({ item }: ListItemProps) => ); interface ProductItemProps { item: any; diff --git a/apps/user-ui/app/(drawer)/(tabs)/stores/store-detail/[id].tsx b/apps/user-ui/app/(drawer)/(tabs)/stores/store-detail/[id].tsx index e8538a8..fa4a3d6 100644 --- a/apps/user-ui/app/(drawer)/(tabs)/stores/store-detail/[id].tsx +++ b/apps/user-ui/app/(drawer)/(tabs)/stores/store-detail/[id].tsx @@ -17,23 +17,12 @@ import ProductCard from "@/components/ProductCard"; import FloatingCartBar from "@/components/floating-cart-bar"; import { useStoreHeaderStore } from "@/src/store/storeHeaderStore"; import { useAllProducts, useStoreWithProducts } from "@/src/hooks/prominent-api-hooks"; +import type { StoreTag, TagChipProps } from "@packages/shared"; const { width: screenWidth } = Dimensions.get("window"); const itemWidth = (screenWidth - 48) / 2; -interface Tag { - id: number; - tagName: string; - productIds?: number[]; -} - -interface ChipProps { - tag: Tag; - isSelected: boolean; - onPress: () => void; -} - -const Chip: React.FC = ({ tag, isSelected, onPress }) => { +const Chip: React.FC = ({ tag, isSelected, onPress }) => { const productCount = tag.productIds?.length || 0; return ( diff --git a/apps/user-ui/components/cart-page.tsx b/apps/user-ui/components/cart-page.tsx index d7d3f84..faa6d31 100644 --- a/apps/user-ui/components/cart-page.tsx +++ b/apps/user-ui/components/cart-page.tsx @@ -28,12 +28,9 @@ import { useCentralProductStore } from '@/src/store/centralProductStore'; import { useCentralSlotStore } from '@/src/store/centralSlotStore'; import { useGetCart, useUpdateCartItem, useRemoveFromCart } from '@/hooks/cart-query-hooks'; import { useGetEssentialConsts } from '@/src/hooks/prominent-api-hooks'; +import type { FlashDeliveryProps } from '@packages/shared'; -interface CartPageProps { - isFlashDelivery?: boolean; -} - -export default function CartPage({ isFlashDelivery = false }: CartPageProps) { +export default function CartPage({ isFlashDelivery = false }: FlashDeliveryProps) { // Hide tabs when cart page is active useHideTabNav(); diff --git a/apps/user-ui/components/checkout-page.tsx b/apps/user-ui/components/checkout-page.tsx index 35aabfe..e9ffc13 100644 --- a/apps/user-ui/components/checkout-page.tsx +++ b/apps/user-ui/components/checkout-page.tsx @@ -16,12 +16,9 @@ import { useGetEssentialConsts } from '@/src/hooks/prominent-api-hooks'; import PaymentAndOrderComponent from '@/components/PaymentAndOrderComponent'; import CheckoutAddressSelector from '@/components/CheckoutAddressSelector'; import { useAddressStore } from '@/src/store/addressStore'; +import type { FlashDeliveryProps } from '@packages/shared'; -interface CheckoutPageProps { - isFlashDelivery?: boolean; -} - -const CheckoutPage: React.FC = ({ isFlashDelivery = false }) => { +const CheckoutPage: React.FC = ({ isFlashDelivery = false }) => { const params = useLocalSearchParams(); const queryClient = useQueryClient(); const router = useRouter(); diff --git a/apps/user-ui/hooks/cart-query-hooks.tsx b/apps/user-ui/hooks/cart-query-hooks.tsx index ca8ac5e..a35af89 100644 --- a/apps/user-ui/hooks/cart-query-hooks.tsx +++ b/apps/user-ui/hooks/cart-query-hooks.tsx @@ -2,6 +2,7 @@ import { useCentralProductStore } from '@/src/store/centralProductStore'; import { useCentralSlotStore } from '@/src/store/centralSlotStore'; import { Alert } from 'react-native'; import { useQuery, useMutation, useQueryClient, UseQueryResult, UseMutationResult } from '@tanstack/react-query'; +import type { CartData as SharedCartData } from '@packages/shared'; import { StorageServiceCasual } from 'common-ui/src/services/StorageServiceCasual'; // Cart type definition @@ -42,11 +43,7 @@ export interface CartItem { slotId: number; } -interface CartData { - items: CartItem[]; - totalItems: number; - totalAmount: number; -} +type CartData = SharedCartData interface UseGetCartOptions { refetchOnWindowFocus?: boolean; diff --git a/apps/web-ui/src/components/FloatingCartBar.tsx b/apps/web-ui/src/components/FloatingCartBar.tsx index e7ff4ac..fd04e80 100644 --- a/apps/web-ui/src/components/FloatingCartBar.tsx +++ b/apps/web-ui/src/components/FloatingCartBar.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react' +import type { FlashDeliveryProps } from '@packages/shared' import { useNavigate } from '@tanstack/react-router' import { useGetCart, useUpdateCartItem, useRemoveFromCart } from '../hooks/cart-query-hooks' import { useAllProducts } from '../hooks/prominent-api-hooks' @@ -7,10 +8,6 @@ import { MiniQuantifier } from 'web-components' import { ShoppingCart, ChevronRight, Clock, X, Package } from 'lucide-react' import dayjs from 'dayjs' -interface FloatingCartBarProps { - isFlashDelivery?: boolean -} - // Smart time window formatting function const formatTimeRange = (deliveryTime: string | Date) => { const time = dayjs(deliveryTime) @@ -28,7 +25,7 @@ const formatTimeRange = (deliveryTime: string | Date) => { return `${time.format('ddd, DD MMM ')}${timeRange}` } -export function FloatingCartBar({ isFlashDelivery = false }: FloatingCartBarProps) { +export function FloatingCartBar({ isFlashDelivery = false }: FlashDeliveryProps) { const navigate = useNavigate() const [isExpanded, setIsExpanded] = useState(false) const [quantities, setQuantities] = useState>({}) diff --git a/apps/web-ui/src/hooks/cart-query-hooks.ts b/apps/web-ui/src/hooks/cart-query-hooks.ts index 789ff1b..3c14e44 100644 --- a/apps/web-ui/src/hooks/cart-query-hooks.ts +++ b/apps/web-ui/src/hooks/cart-query-hooks.ts @@ -1,4 +1,5 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import type { CartData as SharedCartData } from '@packages/shared' type CartType = 'regular' | 'flash' @@ -12,11 +13,7 @@ interface CartItem { deliveryDate?: string | null } -interface CartData { - items: CartItem[] - totalItems: number - totalAmount: number -} +type CartData = SharedCartData function getCartKey(cartType: CartType): string { return `local-cart-${cartType}` diff --git a/apps/web-ui/src/routes/stores.$storeId.tsx b/apps/web-ui/src/routes/stores.$storeId.tsx index 1aafa67..7db1c29 100644 --- a/apps/web-ui/src/routes/stores.$storeId.tsx +++ b/apps/web-ui/src/routes/stores.$storeId.tsx @@ -1,6 +1,7 @@ import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router' import { useStoreWithProducts, useAllProducts } from '../hooks/prominent-api-hooks' import { useState, useMemo } from 'react' +import type { StoreTag, TagChipProps } from '@packages/shared' import { AppLayout } from '../components/AppLayout' import AddToCartDialog from '../components/AddToCartDialog' import { ProductCard } from '../components/ProductCard' @@ -11,12 +12,6 @@ export const Route = createFileRoute('/stores/$storeId')({ component: StoreDetailPage, }) -interface Tag { - id: number - tagName: string - productIds?: number[] -} - function StoreDetailPage() { const { storeId } = useParams({ from: '/stores/$storeId' }) const navigate = useNavigate() @@ -47,7 +42,7 @@ function StoreDetailPage() { // Filter products based on selected tag const filteredProducts = selectedTagId ? storeProducts.filter((product: any) => { - const selectedTag = storeData?.tags.find((t: Tag) => t.id === selectedTagId) + const selectedTag = storeData?.tags.find((t: StoreTag) => t.id === selectedTagId) return selectedTag?.productIds?.includes(product.id) ?? false }) : storeProducts @@ -131,7 +126,7 @@ function StoreDetailPage() { isSelected={selectedTagId === null} onPress={() => setSelectedTagId(null)} /> - {storeData.tags.map((tag: Tag) => ( + {storeData.tags.map((tag: StoreTag) => (

{selectedTagId - ? `${storeData?.tags.find((t: Tag) => t.id === selectedTagId)?.tagName} items` + ? `${storeData?.tags.find((t: StoreTag) => t.id === selectedTagId)?.tagName} items` : `${filteredProducts.length} products`}

@@ -197,12 +192,6 @@ function StoreDetailPage() { ) } -interface TagChipProps { - tag: Tag - isSelected: boolean - onPress: () => void -} - function TagChip({ tag, isSelected, onPress }: TagChipProps) { const productCount = tag.productIds?.length || 0 diff --git a/change-log.txt b/change-log.txt index 5f3823b..fe3aae5 100644 --- a/change-log.txt +++ b/change-log.txt @@ -1039,3 +1039,176 @@ FINAL POSITION on "unify the rest" (06-56): no cluster carries a unify verdict. - apps/fallback-ui/src/stores/userStore.ts: delete local `interface User/Role/Permission` (each exactly {id:number;name:string}); add import type { IdName } from '@packages/shared' + type User = IdName / type Role = IdName / type Permission = IdName. Names kept (UserWithRole extends User + store shape unchanged); only the hook is exported so no external imports affected. [2026-09-05 11:47:00] COMPLETED Cluster-06 IdName adoption. Verification: fallback-ui tsc 107 = baseline, zero errors in userStore.ts. Docs: cluster-06 file updated. + +================================================================================ +2026-09-05 12:20:00 — TYPE-CLUSTER UNIFICATION (all remaining duplicated types → @packages/shared; db_helper_postgres excluded per user) +Scope decisions: schema-bound sqlite mirrors stay local (no remaining dup once pg excluded); backend-derived BaseProduct/AvailabilityEntry/MergedProduct (c23-25) stay local; ConstKey (c22) stays local; c44/49 Tag gets qualified shared name `StoreTag`. + +[NEW FILE] packages/shared/types/ui-common.types.ts — single source for common-ui ↔ web-components duplicated component prop types (clusters 11,12,13,14,47,51,52,53,54,56): ++ import type { ReactNode } from 'react' ++ export interface DropdownOption { label: string; value: string | number } ++ export interface DialogProps { open: boolean; onClose: () => void; children: ReactNode; enableDismiss?: boolean } ++ export interface ConfirmationDialogProps { open: boolean; positiveAction: (comment?: string) => void; commentNeeded?: boolean; negativeAction?: () => void; title?: string; message?: string; confirmText?: string; cancelText?: string; isLoading?: boolean } ++ export interface LoadingDialogProps { open: boolean; message?: string } ++ export interface QuantifierProps { value: number; setValue: (value: number) => void; step?: number; unit?: string | { shortNotation: string }; min?: number; max?: number } ++ export interface ImageUploaderProps { images: { uri?: string }[]; onAddImage: () => void; onRemoveImage: (uri: string) => void; existingImageUrls?: string[]; onRemoveExistingImage?: (url: string) => void; allowMultiple?: boolean } ++ export interface ImageUploaderNeoItem { imgUrl: string; mimeType: string | null } ++ export interface ImageUploaderNeoPayload { url: string; mimeType: string | null } ++ export interface ImageUploaderNeoProps { images: ImageUploaderNeoItem[]; onImageAdd: (images: ImageUploaderNeoPayload[]) => void; onImageRemove: (image: ImageUploaderNeoPayload) => void; allowMultiple?: boolean } ++ export interface TextButtonProps { text: string } + +[EDIT] packages/shared/types/app-common.types.ts: ++ export interface FlashDeliveryProps { isFlashDelivery?: boolean } (c8: web-ui FloatingCartBarProps, user-ui CheckoutPageProps/CartPageProps) ++ export interface ListItemProps { item: any } (c9: user-ui RenderStoreProps/SlotItemProps) +- export interface ComplaintItemProps { item: any } ++ export type ComplaintItemProps = ListItemProps (canonical shape kept under old name) ++ export interface CelebrationProps { isCelebrating: boolean } (c48: fallback-ui CharactersProps/SceneProps) + +[EDIT] packages/shared/types/order.types.ts: ++ export type OrderStatus = 'cancelled' | 'success' (c20: backend order.ts 2 function-scoped copies) ++ export type DeliveryStatus = 'cancelled' | 'success' | 'pending' | 'packaged' (c21: same file) ++ export interface CartData { items: T[]; totalItems: number; totalAmount: number } (c50: element CartItem differs per app → generic) + +[EDIT] packages/shared/types/const.types.ts: ++ export type ConstValueType = 'string' | 'boolean' | 'number' (c19: backend + sqlite const-keys.ts) + +[EDIT] packages/shared/types/admin.ts: ++ export type { ConstValueType } from './const.types' (added to existing const re-export line — barrel surface unchanged) + +[EDIT] packages/shared/types/cache.types.ts (c17): +- stale note "BannerData ... intentionally NOT included" replaced: pg copy remains local by exclusion, sqlite + backend now share. ++ export interface BannerData { id: number; name: string; imageUrl: string | null; serialNum: number | null; skuIds: number[] | null; createdAt: Date } + +[EDIT] packages/shared/types/store.types.ts (c44/c49): ++ export interface StoreTag { id: number; tagName: string; productIds?: number[] } (qualified name — avoids clash with richer backend Tag) ++ export interface TagChipProps { tag: StoreTag; isSelected: boolean; onPress: () => void } + +[EDIT] packages/shared/types/admin.ts (c7): ++ export interface SkuFeatureLike { featureName?: string | null; featureValue: string } (was sqlite CreateSkuFeatureInput + SkuFeatureLike) + +[EDIT] packages/shared/types/index.ts: + export type * from './ui-common.types'; stale comments corrected (DropdownOption/CartData now real; CartIconProps comment fixed). + +2026-09-05 12:26:00 — Adopt shared ui-common types in packages/ui (common-ui) + packages/web-components (clusters 11,12,13,14,47,51,52,53,54,56). All replacements are `import type` (runtime-erased). Local prop interfaces deleted; public re-exports preserved. + +packages/ui/src/components/dialog.tsx: +- lines 7-12 interface DialogProps {...} → import type { ConfirmationDialogProps, DialogProps } from '@packages/shared' +- lines 194-204 interface ConfirmationDialogProps {...} (deleted; shared import above) +- line 1: ReactNode named import dropped (only DialogProps used it): import React, { useState } from 'react' + +packages/ui/src/components/loading-dialog.tsx: +- lines 3-5 interface LoadingDialogProps → import type { LoadingDialogProps } from '@packages/shared' + +packages/ui/src/components/quantifier.tsx: +- lines 7-13 interface QuantifierProps → import type { QuantifierProps } from '@packages/shared' (Quantifier + MiniQuantifier both use it) + +packages/ui/src/components/ImageUploader.tsx: +- lines 9-16 interface ImageUploaderProps (+ trailing comments) → import type { ImageUploaderProps } from '@packages/shared' + +packages/ui/src/components/ImageUploaderNeo.tsx: +- lines 11-27: local ImageUploaderNeoItem / ImageUploaderNeoPayload / ImageUploaderNeoProps deleted ++ import type { ImageUploaderNeoItem, ImageUploaderNeoPayload, ImageUploaderNeoProps } from '@packages/shared' ++ export type { ImageUploaderNeoItem, ImageUploaderNeoPayload } from '@packages/shared' (keeps packages/ui/index.ts:22,91-92 re-export surface intact) + +packages/ui/src/components/dropdown.tsx: +- lines 6-9 export interface DropdownOption → import type { DropdownOption } from '@packages/shared' + export type { DropdownOption } from '@packages/shared' (public surface kept) + NOTE: bottom-dropdown.tsx (+disabled) and multi-select.tsx (value: string) are DIFFERENT shapes — intentionally untouched. + +packages/ui/src/components/button.tsx: +- lines 74-76 interface MyTextButtonProps extends Omit { text: string } ++ import type { TextButtonProps } from '@packages/shared' (top) ++ interface MyTextButtonProps extends Omit, TextButtonProps {} + +packages/web-components/tsconfig.json: ++ paths: "@packages/shared": ["../shared"], "@packages/shared/*": ["../shared/*"] (type-only imports resolve; no runtime dep) + +packages/web-components/src/components/dialog.tsx: +- lines 6-12 interface BottomDialogProps {...} → import type { ConfirmationDialogProps, DialogProps } from '@packages/shared'; BottomDialog annotated : DialogProps +- lines 55-65 interface ConfirmationDialogProps {...} (deleted) + +packages/web-components/src/components/loading-dialog.tsx: +- lines 3-5 interface LoadingDialogProps → import type { LoadingDialogProps } from '@packages/shared' + +packages/web-components/src/components/quantifier.tsx: +- lines 6-12 interface QuantifierProps → import type { QuantifierProps } from '@packages/shared' (Quantifier + MiniQuantifier) + +packages/web-components/src/components/image-uploader.tsx: +- lines 5-12 interface ImageUploaderProps → import type { ImageUploaderProps } from '@packages/shared' + +packages/web-components/src/components/image-uploader-neo.tsx: +- lines 6-19: local ImageUploaderNeoItem / ImageUploaderNeoPayload / ImageUploaderNeoProps deleted ++ import type { ImageUploaderNeoItem, ImageUploaderNeoPayload, ImageUploaderNeoProps } from '@packages/shared' ++ export type { ImageUploaderNeoItem, ImageUploaderNeoPayload } from '@packages/shared' (keeps src/index.ts:19 re-export intact) + +packages/web-components/src/components/dropdown.tsx: +- lines 5-8 export interface DropdownOption → import type { DropdownOption } from '@packages/shared' + export type { DropdownOption } from '@packages/shared' (keeps src/index.ts:24 intact) + +packages/web-components/src/components/my-button.tsx: +- lines 46-48 interface pButtonProps extends Omit { text: string } ++ import type { TextButtonProps } from '@packages/shared' (top) ++ interface pButtonProps extends Omit, TextButtonProps {} + +2026-09-05 12:34:00 — Adopt shared types across apps + db_helper_sqlite (clusters 3,7,8,9,17,19,20,21,44,48,49,50). db_helper_postgres untouched throughout. + +apps/user-ui/components/checkout-page.tsx: +- lines 20-22 interface CheckoutPageProps { isFlashDelivery?: boolean } ++ import type { FlashDeliveryProps } from '@packages/shared' (top); FC generic uses FlashDeliveryProps + +apps/user-ui/components/cart-page.tsx: +- lines 32-34 interface CartPageProps { isFlashDelivery?: boolean } → same shared FlashDeliveryProps import + +apps/user-ui/app/(drawer)/(tabs)/home/index.tsx: +- line 71-73 interface RenderStoreProps { item: any } → import type { ListItemProps } from '@packages/shared'; RenderStore uses ListItemProps +- line 346-348 interface SlotItemProps { item: any } → SlotItem uses ListItemProps + +apps/user-ui/app/(drawer)/(tabs)/stores/store-detail/[id].tsx: +- lines 24-28 interface Tag { id; tagName; productIds? } → import type { StoreTag, TagChipProps } from '@packages/shared'; local `Tag` refs replaced with StoreTag +- lines 30-34 interface ChipProps { tag; isSelected; onPress } → Chip uses shared TagChipProps + +apps/user-ui/hooks/cart-query-hooks.tsx: +- lines 45-49 interface CartData { items: CartItem[]; totalItems; totalAmount } ++ import type { CartData as SharedCartData } from '@packages/shared'; type CartData = SharedCartData (element differs per app — generic instantiation) + +apps/web-ui/src/components/FloatingCartBar.tsx: +- lines 10-12 interface FloatingCartBarProps { isFlashDelivery?: boolean } → import type { FlashDeliveryProps } from '@packages/shared' + +apps/web-ui/src/routes/stores.$storeId.tsx: +- lines 14-18 interface Tag → shared StoreTag (local refs at lines 50,134 updated) +- lines 200-204 interface TagChipProps → shared TagChipProps (tag: StoreTag) + +apps/web-ui/src/hooks/cart-query-hooks.ts: +- lines 15-19 interface CartData → type CartData = SharedCartData (same generic pattern as user-ui) + +apps/backend/src/trpc/apis/user-apis/apis/order.ts: +- lines 337-338 + 477-478: function-scoped `type DeliveryStatus` / `type OrderStatus` duplicates deleted (2 pairs) ++ import type { DeliveryStatus, OrderStatus } from '@packages/shared' + +apps/backend/src/lib/const-keys.ts (c19): +- line 55 export type ConstValueType = 'string' | 'boolean' | 'number' → import type { ConstValueType } from '@packages/shared' (no external importers — verified grep) + +apps/backend/src/stores/banner-store.ts (c17): +- lines 11-18 interface Banner {...} → import type { BannerData } from '@packages/shared'; Promise/Promise signatures now use BannerData +- line 6: `type BannerData` removed from '@/src/dbService' import list (now from shared; shape identical) + +packages/db_helper_sqlite/src/lib/const-keys.ts (c19): +- line 57 export type ConstValueType → import type { ConstValueType } from '@packages/shared' + +packages/db_helper_sqlite/src/user-apis/slots.ts + src/admin-apis/vendor-snippets.ts (c3, schema-bound → single-sourced INSIDE sqlite, not shared): +- vendor-snippets.ts line 15 type DeliverySlotRow = InferSelectModel ++ import type { SlotRow } from '../user-apis/slots' ; type DeliverySlotRow = SlotRow +- slots.ts line 7: type SlotRow = ... → export type SlotRow = ... (single source of the alias) + +packages/db_helper_sqlite/src/lib/sku-features.ts + src/admin-apis/product.ts (c7): +- sku-features.ts lines 1-4 export interface SkuFeatureLike → import type { SkuFeatureLike } from '@packages/shared' + export type { SkuFeatureLike } from '@packages/shared' +- product.ts lines 69-72 interface CreateSkuFeatureInput → import adds SkuFeatureLike; line 90 features: CreateSkuFeatureInput[] → features: SkuFeatureLike[] + +packages/db_helper_sqlite/src/stores/store-helpers.ts (c17): +- lines 33-40 export interface BannerData → added to existing shared re-export line: export type { ProductTagData, TagProductMapping, UserNegativityData, BannerData } from '@packages/shared'; BannerData added to type import (used by getAllBannersForCache) + +apps/admin-ui/src/components/ProductForm.tsx (c7): +- lines 9-12 interface Attribute { featureName: string | null; featureValue: string } → import type { SkuFeatureLike } from '@packages/shared'; type Attribute = SkuFeatureLike (name kept for ~10 in-file refs; optional featureName is a safe widening — verified all uses) + +apps/fallback-ui/src/components/3d/Characters.tsx + 3d/Scene.tsx (c48): +- local CharactersProps / SceneProps { isCelebrating: boolean } → import type { CelebrationProps } from '@packages/shared' + +[2026-09-05 12:55:00] COMPLETED type-cluster unification pass (clusters 3,7,8,9,11,12,13,14,17,19,20,21,44,47,48,49,50,51,52,53,54,56 → @packages/shared). +Verification (tsc --noEmit error counts vs pre-change baselines): backend 11=11, web-ui 150=150, user-ui 106=106, admin-ui 122=122, fallback-ui 107=107, db_helper_sqlite 4=4. Diffs are line-number shifts only (deleted lines) — zero new errors. `apps/web-ui` vite build exit 0. db_helper_postgres: untouched. Remaining same-name locals verified genuinely different shapes (bottom-dropdown/multi-select DropdownOption variants, web-ui Dialog.tsx DialogProps, user-ui floating-cart-bar props) — intentionally left. type-clusters/README.md verdicts updated to ✅ for the resolved clusters with a pass note at top. diff --git a/packages/db_helper_sqlite/src/admin-apis/product.ts b/packages/db_helper_sqlite/src/admin-apis/product.ts index 2f18865..447fd29 100644 --- a/packages/db_helper_sqlite/src/admin-apis/product.ts +++ b/packages/db_helper_sqlite/src/admin-apis/product.ts @@ -58,6 +58,7 @@ import type { AdminSpecialDeal, AdminUnit, AdminUpdateSlotProductsResult, + SkuFeatureLike, Store, } from '@packages/shared' @@ -66,11 +67,6 @@ type SkuRow = InferSelectModel type MarketStatsRow = InferSelectModel type SkuFeatureRow = InferSelectModel -interface CreateSkuFeatureInput { - featureName?: string | null - featureValue: string -} - interface CreateComboItemInput { skuId: number } @@ -87,7 +83,7 @@ interface CreateSkuInput { isOffer?: boolean isComboOnly?: boolean isDeleted?: boolean - features: CreateSkuFeatureInput[] + features: SkuFeatureLike[] comboItems?: CreateComboItemInput[] } diff --git a/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts b/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts index 21e91e5..3afe623 100644 --- a/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts +++ b/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts @@ -10,9 +10,10 @@ import type { AdminVendorUpdatePackagingResult, } from '@packages/shared' import { coerceDate } from '../lib/date' +import type { SlotRow } from '../user-apis/slots' type VendorSnippetRow = InferSelectModel -type DeliverySlotRow = InferSelectModel +type DeliverySlotRow = SlotRow const mapVendorSnippet = (snippet: VendorSnippetRow): AdminVendorSnippet => ({ id: snippet.id, diff --git a/packages/db_helper_sqlite/src/lib/const-keys.ts b/packages/db_helper_sqlite/src/lib/const-keys.ts index 2c61b25..53e0583 100644 --- a/packages/db_helper_sqlite/src/lib/const-keys.ts +++ b/packages/db_helper_sqlite/src/lib/const-keys.ts @@ -1,3 +1,5 @@ +import type { ConstValueType } from '@packages/shared' + export const CONST_KEYS = { minRegularOrderValue: 'minRegularOrderValue', freeDeliveryThreshold: 'freeDeliveryThreshold', @@ -54,8 +56,6 @@ export type ConstKey = (typeof CONST_KEYS)[keyof typeof CONST_KEYS] export const CONST_KEYS_ARRAY = Object.values(CONST_KEYS) as ConstKey[] -export type ConstValueType = 'string' | 'boolean' | 'number' - export const CONST_TYPES: Record = { minRegularOrderValue: 'number', freeDeliveryThreshold: 'number', diff --git a/packages/db_helper_sqlite/src/lib/sku-features.ts b/packages/db_helper_sqlite/src/lib/sku-features.ts index cebd70f..5858ba9 100644 --- a/packages/db_helper_sqlite/src/lib/sku-features.ts +++ b/packages/db_helper_sqlite/src/lib/sku-features.ts @@ -1,7 +1,6 @@ -export interface SkuFeatureLike { - featureName?: string | null - featureValue: string -} +import type { SkuFeatureLike } from '@packages/shared' + +export type { SkuFeatureLike } from '@packages/shared' export const cleanFeatureValue = (value: string): string => value.replace(/\.0(?=\D|$)/g, '') diff --git a/packages/db_helper_sqlite/src/stores/store-helpers.ts b/packages/db_helper_sqlite/src/stores/store-helpers.ts index 788a297..fe4ca2f 100644 --- a/packages/db_helper_sqlite/src/stores/store-helpers.ts +++ b/packages/db_helper_sqlite/src/stores/store-helpers.ts @@ -17,11 +17,11 @@ import { } from '../db/schema' import { eq, and, gt, sql, isNotNull, asc } from 'drizzle-orm' import { composeSkuName, composeUnitNotation } from '../lib/sku-features' -import type { ProductTagData, TagProductMapping, UserNegativityData, StoreSummary } from '@packages/shared' +import type { ProductTagData, TagProductMapping, UserNegativityData, StoreSummary, BannerData } from '@packages/shared' // Re-exported so the package index (`export { ... } from './src/stores/store-helpers'`) // keeps resolving to the single shared source. -export type { ProductTagData, TagProductMapping, UserNegativityData } from '@packages/shared' +export type { ProductTagData, TagProductMapping, UserNegativityData, BannerData } from '@packages/shared' // StoreBasicData is exactly the shared StoreSummary shape — single source, same export name. export type StoreBasicData = StoreSummary @@ -30,15 +30,6 @@ export type StoreBasicData = StoreSummary // BANNER STORE HELPERS // ============================================================================ -export interface BannerData { - id: number - name: string - imageUrl: string | null - serialNum: number | null - skuIds: number[] | null - createdAt: Date -} - export async function getAllBannersForCache(): Promise { return db.query.homeBanners.findMany({ where: isNotNull(homeBanners.serialNum), diff --git a/packages/db_helper_sqlite/src/user-apis/slots.ts b/packages/db_helper_sqlite/src/user-apis/slots.ts index f631ffd..9624f1e 100644 --- a/packages/db_helper_sqlite/src/user-apis/slots.ts +++ b/packages/db_helper_sqlite/src/user-apis/slots.ts @@ -4,7 +4,7 @@ import { asc, eq } from 'drizzle-orm' import type { InferSelectModel } from 'drizzle-orm' import type { UserDeliverySlot, UserSlotAvailability } from '@packages/shared' -type SlotRow = InferSelectModel +export type SlotRow = InferSelectModel const mapSlot = (slot: SlotRow): UserDeliverySlot => ({ id: slot.id, diff --git a/packages/shared/types/admin.ts b/packages/shared/types/admin.ts index a99178e..df008bf 100644 --- a/packages/shared/types/admin.ts +++ b/packages/shared/types/admin.ts @@ -5,7 +5,7 @@ import type { MessageResponse, BasicSuccessResponse, IdName } from './primitives.types' export type { Banner } from './banner.types' export type { Complaint, ComplaintWithUser } from './complaint.types' -export type { Constant, ConstantUpdateResult } from './const.types' +export type { Constant, ConstantUpdateResult, ConstValueType } from './const.types' export type { CouponValidationResult, UserMiniInfo } from './coupon.types' export type { StaffRole } from './staff-user.types' export type { MessageResponse, IdName } from './primitives.types' @@ -317,6 +317,13 @@ export interface AdminSkuFeature { featureValue: string; } +// Feature input shape — was duplicated as CreateSkuFeatureInput (db_helper_sqlite +// admin-apis/product.ts) and SkuFeatureLike (db_helper_sqlite lib/sku-features.ts). +export interface SkuFeatureLike { + featureName?: string | null; + featureValue: string; +} + export interface AdminProductComboItem { skuId: number; skuName: string | null; diff --git a/packages/shared/types/app-common.types.ts b/packages/shared/types/app-common.types.ts index 0b307b5..f3a84ba 100644 --- a/packages/shared/types/app-common.types.ts +++ b/packages/shared/types/app-common.types.ts @@ -37,11 +37,24 @@ export interface AddressFormProps { isEdit?: boolean } -// Complaint item — identical in user-ui + web-ui complaints lists -export interface ComplaintItemProps { +// Generic list-item renderer props — user-ui home RenderStoreProps/SlotItemProps +export interface ListItemProps { item: any } +// Complaint item — identical in user-ui + web-ui complaints lists +export type ComplaintItemProps = ListItemProps + +// Page-level flash-delivery flag — web-ui FloatingCartBar + user-ui checkout/cart pages +export interface FlashDeliveryProps { + isFlashDelivery?: boolean +} + +// 3D celebration flag — fallback-ui CharactersProps/SceneProps +export interface CelebrationProps { + isCelebrating: boolean +} + // Compact product card — identical in web-ui flash.tsx + slot-view.tsx export interface CompactProductCardProps { item: any diff --git a/packages/shared/types/cache.types.ts b/packages/shared/types/cache.types.ts index ead6714..a5d5df7 100644 --- a/packages/shared/types/cache.types.ts +++ b/packages/shared/types/cache.types.ts @@ -1,10 +1,8 @@ /** * Cache / Store-Helper Types - * Identical in db_helper_postgres and db_helper_sqlite `src/stores/store-helpers.ts`. - * Centralized here so both helpers + backend stores import one source. - * NOTE: BannerData/ProductBasicData are intentionally NOT included here — they - * diverge between pg (productIds/flat product) and sqlite (skuIds/SKU-composed shape). - * Only the truly identical subset is shared; divergent shapes keep their local defs. + * Identical in db_helper_sqlite `src/stores/store-helpers.ts` (pg keeps its own + * divergent copies by exclusion). Centralized here so helpers + backend stores + * import one source. */ export interface ProductTagData { @@ -21,3 +19,13 @@ export interface UserNegativityData { userId: number totalNegativityScore: number } + +// Banner cache row — sqlite store-helpers + backend banner-store (was local `Banner`) +export interface BannerData { + id: number + name: string + imageUrl: string | null + serialNum: number | null + skuIds: number[] | null + createdAt: Date +} diff --git a/packages/shared/types/const.types.ts b/packages/shared/types/const.types.ts index b1db5e4..b4c74d8 100644 --- a/packages/shared/types/const.types.ts +++ b/packages/shared/types/const.types.ts @@ -13,3 +13,6 @@ export interface ConstantUpdateResult { updatedCount: number; keys: string[]; } + +// Value union for CONST_TYPES maps — was duplicated in backend + sqlite const-keys.ts +export type ConstValueType = 'string' | 'boolean' | 'number' diff --git a/packages/shared/types/index.ts b/packages/shared/types/index.ts index f22497b..306a305 100644 --- a/packages/shared/types/index.ts +++ b/packages/shared/types/index.ts @@ -9,10 +9,11 @@ export type * from './user'; export type * from './store.types'; // Shared cross-cutting types — import from '@packages/shared' anywhere: -export type * from './primitives.types'; // MessageResponse, BasicSuccessResponse, IdName, ReactParentComponent +export type * from './primitives.types'; // MessageResponse, BasicSuccessResponse, IdName, ReactParentComponent, TabIconProps export type * from './upload.types'; // ContextString, UploadInput, UploadBatchInput, UploadResult -export type * from './app-common.types'; // LoginFormInputs, DropdownOption, CartIconProps, AddressFormProps, CartData -export type * from './order.types'; // PlacedOrder, CouponUsageWithCoupon, GetAllOrdersInput +export type * from './app-common.types'; // LoginFormInputs, ComplaintItemProps/ListItemProps, FlashDeliveryProps, CelebrationProps +export type * from './ui-common.types'; // DropdownOption, DialogProps, ConfirmationDialogProps, LoadingDialogProps, QuantifierProps, ImageUploader*, TextButtonProps +export type * from './order.types'; // PlacedOrder, CouponUsageWithCoupon, GetAllOrdersInput, OrderStatus, DeliveryStatus, CartData export type * from './seed.types'; // UnitSeedData, StaffRoleName, StaffPermissionName, RolePermissionAssignment, KeyValSeedData export type * from './inputs.types'; // CreateBannerInput, UpdateBannerInput, CreateStoreInput, Login/Register requests -export type * from './cache.types'; // ProductTagData, TagProductMapping, UserNegativityData (truly identical subset) +export type * from './cache.types'; // ProductTagData, TagProductMapping, UserNegativityData, BannerData diff --git a/packages/shared/types/order.types.ts b/packages/shared/types/order.types.ts index abe6701..b190c2c 100644 --- a/packages/shared/types/order.types.ts +++ b/packages/shared/types/order.types.ts @@ -47,3 +47,17 @@ export type GetAllOrdersInput = { cancellationFilter?: 'all' | 'cancelled' | 'not_cancelled' flashDeliveryFilter?: 'all' | 'flash' | 'regular' } + +// Cancellation-flow status unions — were duplicated as function-scoped locals +// in backend user-apis order.ts (two closures). Single source here. +export type OrderStatus = 'cancelled' | 'success' + +export type DeliveryStatus = 'cancelled' | 'success' | 'pending' | 'packaged' + +// Cart summary shape — identical in user-ui + web-ui cart hooks, but the +// element CartItem differs per app, so the element type is generic. +export interface CartData { + items: T[] + totalItems: number + totalAmount: number +} diff --git a/packages/shared/types/store.types.ts b/packages/shared/types/store.types.ts index 10c3afd..52b3d3a 100644 --- a/packages/shared/types/store.types.ts +++ b/packages/shared/types/store.types.ts @@ -20,3 +20,22 @@ export interface StoreSummary { export interface StoresSummaryResponse { stores: StoreSummary[]; } + +/** + * Lightweight tag row used on store-detail pages (user-ui + web-ui). + * Named StoreTag to avoid clashing with the richer backend Tag shape. + */ +export interface StoreTag { + id: number; + tagName: string; + productIds?: number[]; +} + +/** + * Chip props for tag selection on store-detail pages. + */ +export interface TagChipProps { + tag: StoreTag; + isSelected: boolean; + onPress: () => void; +} diff --git a/packages/ui/src/components/ImageUploader.tsx b/packages/ui/src/components/ImageUploader.tsx index 2c0c89c..97c82ec 100755 --- a/packages/ui/src/components/ImageUploader.tsx +++ b/packages/ui/src/components/ImageUploader.tsx @@ -1,20 +1,12 @@ import React from "react"; import { View, TouchableOpacity } from "react-native"; import { Image } from 'expo-image'; +import type { ImageUploaderProps } from '@packages/shared'; import MyText from "./text"; import tw from '../lib/tailwind'; import Ionicons from "@expo/vector-icons/Ionicons"; import { MaterialIcons } from "@expo/vector-icons"; -interface ImageUploaderProps { - images: { uri?: string }[]; - onAddImage: () => void; - onRemoveImage: (uri: string) => void; - existingImageUrls?: string[]; // URLs of existing images that should be displayed - onRemoveExistingImage?: (url: string) => void; // Callback to track which existing images are removed - allowMultiple?: boolean; // Whether to allow multiple image uploads -} - const ImageUploader: React.FC = ({ images, existingImageUrls = [], diff --git a/packages/ui/src/components/ImageUploaderNeo.tsx b/packages/ui/src/components/ImageUploaderNeo.tsx index a2a7d12..eb5e5d7 100644 --- a/packages/ui/src/components/ImageUploaderNeo.tsx +++ b/packages/ui/src/components/ImageUploaderNeo.tsx @@ -3,27 +3,13 @@ import { View } from 'react-native' import { Image } from 'expo-image' import Ionicons from '@expo/vector-icons/Ionicons' import { MaterialIcons } from '@expo/vector-icons' +import type { ImageUploaderNeoItem, ImageUploaderNeoPayload, ImageUploaderNeoProps } from '@packages/shared' import tw from '../lib/tailwind' import MyText from './text' import MyTouchableOpacity from './touchable-opacity' import usePickImage from './use-pick-image' -export interface ImageUploaderNeoItem { - imgUrl: string - mimeType: string | null -} - -export interface ImageUploaderNeoPayload { - url: string - mimeType: string | null -} - -interface ImageUploaderNeoProps { - images: ImageUploaderNeoItem[] - onImageAdd: (images: ImageUploaderNeoPayload[]) => void - onImageRemove: (image: ImageUploaderNeoPayload) => void - allowMultiple?: boolean -} +export type { ImageUploaderNeoItem, ImageUploaderNeoPayload } from '@packages/shared' const ImageUploaderNeo: React.FC = ({ images, diff --git a/packages/ui/src/components/button.tsx b/packages/ui/src/components/button.tsx index c4ecad5..b8b46c8 100755 --- a/packages/ui/src/components/button.tsx +++ b/packages/ui/src/components/button.tsx @@ -1,6 +1,7 @@ import React from "react"; import { Button as PaperButton, ButtonProps } from "react-native-paper"; // import { useTheme } from "../hooks/theme-context"; +import type { TextButtonProps } from '@packages/shared'; import { theme, useTheme } from "common-ui"; import { TouchableOpacity } from "react-native"; import MyText from "./text"; // Updated import path @@ -70,9 +71,7 @@ function MyButton({ } // MyTextButton props: same as Props, but no children and with an extra 'text' property -interface MyTextButtonProps extends Omit { - text: string; -} +interface MyTextButtonProps extends Omit, TextButtonProps {} export function MyTextButton({ variant = "blue", diff --git a/packages/ui/src/components/dialog.tsx b/packages/ui/src/components/dialog.tsx index f2c2ca9..37adbbe 100755 --- a/packages/ui/src/components/dialog.tsx +++ b/packages/ui/src/components/dialog.tsx @@ -1,16 +1,10 @@ -import React, { ReactNode, useState } from 'react'; +import React, { useState } from 'react'; import { Modal, View, TouchableOpacity, StyleSheet, Animated, Easing, Dimensions, TextInput, KeyboardAvoidingView, Platform, ScrollView } from 'react-native'; +import type { ConfirmationDialogProps, DialogProps } from '@packages/shared'; import MyText from './text'; import MyButton from './button'; import tw from "../lib/tailwind"; -interface DialogProps { - open: boolean; - onClose: () => void; - children: ReactNode; - enableDismiss?: boolean; -} - const SCREEN_HEIGHT = Dimensions.get('window').height; export const BottomDialog: React.FC = ({ open, onClose, children, enableDismiss = true }) => { @@ -191,18 +185,6 @@ const commentStyles = StyleSheet.create({ export default BottomDialog; -interface ConfirmationDialogProps { - open: boolean; - positiveAction: (comment?: string) => void; - commentNeeded?: boolean; - negativeAction?: () => void; - title?: string; - message?: string; - confirmText?: string; - cancelText?: string; - isLoading?: boolean; -} - export const ConfirmationDialog: React.FC = (props) => { const { open, diff --git a/packages/ui/src/components/dropdown.tsx b/packages/ui/src/components/dropdown.tsx index 23633e3..b44b43c 100755 --- a/packages/ui/src/components/dropdown.tsx +++ b/packages/ui/src/components/dropdown.tsx @@ -2,11 +2,9 @@ import tw from "../lib/tailwind"; import React from "react"; import { Text, View } from "react-native"; import { Dropdown } from "react-native-element-dropdown"; +import type { DropdownOption } from '@packages/shared'; -export interface DropdownOption { - label: string; - value: string | number; -} +export type { DropdownOption } from '@packages/shared'; interface Props { label: string; diff --git a/packages/ui/src/components/loading-dialog.tsx b/packages/ui/src/components/loading-dialog.tsx index 7f08e8e..d308e39 100644 --- a/packages/ui/src/components/loading-dialog.tsx +++ b/packages/ui/src/components/loading-dialog.tsx @@ -1,13 +1,9 @@ import React from 'react'; import { View, ActivityIndicator, Modal, TouchableOpacity } from 'react-native'; +import type { LoadingDialogProps } from '@packages/shared'; import tw from '../lib/tailwind'; import MyText from './text'; -interface LoadingDialogProps { - open: boolean; - message?: string; -} - export const LoadingDialog: React.FC = ({ open, message = 'Loading...' diff --git a/packages/ui/src/components/quantifier.tsx b/packages/ui/src/components/quantifier.tsx index 7bb1f11..abc0bf7 100644 --- a/packages/ui/src/components/quantifier.tsx +++ b/packages/ui/src/components/quantifier.tsx @@ -1,18 +1,10 @@ import React from 'react'; import { View, TouchableOpacity, Text } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; +import type { QuantifierProps } from '@packages/shared'; import tw from '../lib/tailwind'; import { colors } from '../lib/theme-colors'; -interface QuantifierProps { - value: number; - setValue: (value: number) => void; - step?: number; - unit?: string | { shortNotation: string }; - min?: number; - max?: number; -} - const Quantifier: React.FC = ({ value, setValue, diff --git a/packages/web-components/src/components/dialog.tsx b/packages/web-components/src/components/dialog.tsx index 15ab6b9..dc18640 100644 --- a/packages/web-components/src/components/dialog.tsx +++ b/packages/web-components/src/components/dialog.tsx @@ -1,21 +1,15 @@ import React, { useEffect, useState } from 'react' +import type { ConfirmationDialogProps, DialogProps } from '@packages/shared' import { p } from './my-text' import { MyButton } from './my-button' import { cn } from '../lib/utils' -interface BottomDialogProps { - open: boolean - onClose: () => void - children: React.ReactNode - enableDismiss?: boolean -} - export function BottomDialog({ open, onClose, children, enableDismiss = true, -}: BottomDialogProps) { +}: DialogProps) { const [visible, setVisible] = useState(false) useEffect(() => { @@ -52,18 +46,6 @@ export function BottomDialog({ ) } -interface ConfirmationDialogProps { - open: boolean - positiveAction: (comment?: string) => void - commentNeeded?: boolean - negativeAction?: () => void - title?: string - message?: string - confirmText?: string - cancelText?: string - isLoading?: boolean -} - export function ConfirmationDialog({ open, positiveAction, diff --git a/packages/web-components/src/components/dropdown.tsx b/packages/web-components/src/components/dropdown.tsx index fb8462c..867f1b4 100644 --- a/packages/web-components/src/components/dropdown.tsx +++ b/packages/web-components/src/components/dropdown.tsx @@ -1,11 +1,9 @@ import React from 'react' import { cn } from '../lib/utils' import { ChevronDown } from 'lucide-react' +import type { DropdownOption } from '@packages/shared' -export interface DropdownOption { - label: string - value: string | number -} +export type { DropdownOption } from '@packages/shared' interface DropdownProps { label: string diff --git a/packages/web-components/src/components/image-uploader-neo.tsx b/packages/web-components/src/components/image-uploader-neo.tsx index cf89cd4..c6dc3da 100644 --- a/packages/web-components/src/components/image-uploader-neo.tsx +++ b/packages/web-components/src/components/image-uploader-neo.tsx @@ -1,24 +1,10 @@ import React, { useRef } from 'react' import { cn } from '../lib/utils' import { Plus, X } from 'lucide-react' +import type { ImageUploaderNeoItem, ImageUploaderNeoPayload, ImageUploaderNeoProps } from '@packages/shared' import { div } from './my-touchable-opacity' -export interface ImageUploaderNeoItem { - imgUrl: string - mimeType: string | null -} - -export interface ImageUploaderNeoPayload { - url: string - mimeType: string | null -} - -interface ImageUploaderNeoProps { - images: ImageUploaderNeoItem[] - onImageAdd: (images: ImageUploaderNeoPayload[]) => void - onImageRemove: (image: ImageUploaderNeoPayload) => void - allowMultiple?: boolean -} +export type { ImageUploaderNeoItem, ImageUploaderNeoPayload } from '@packages/shared' function usePickImage({ multiple, diff --git a/packages/web-components/src/components/image-uploader.tsx b/packages/web-components/src/components/image-uploader.tsx index 87adfe2..7b1aa3b 100644 --- a/packages/web-components/src/components/image-uploader.tsx +++ b/packages/web-components/src/components/image-uploader.tsx @@ -1,15 +1,8 @@ import React from 'react' import { cn } from '../lib/utils' import { Plus, X } from 'lucide-react' +import type { ImageUploaderProps } from '@packages/shared' -interface ImageUploaderProps { - images: { uri?: string }[] - onAddImage: () => void - onRemoveImage: (uri: string) => void - existingImageUrls?: string[] - onRemoveExistingImage?: (url: string) => void - allowMultiple?: boolean -} export function ImageUploader({ images, diff --git a/packages/web-components/src/components/loading-dialog.tsx b/packages/web-components/src/components/loading-dialog.tsx index e81f75c..e33d7ad 100644 --- a/packages/web-components/src/components/loading-dialog.tsx +++ b/packages/web-components/src/components/loading-dialog.tsx @@ -1,9 +1,5 @@ import React from 'react' - -interface LoadingDialogProps { - open: boolean - message?: string -} +import type { LoadingDialogProps } from '@packages/shared' export function LoadingDialog({ open, message = 'Loading...' }: LoadingDialogProps) { if (!open) return null diff --git a/packages/web-components/src/components/my-button.tsx b/packages/web-components/src/components/my-button.tsx index cdd8aeb..1b9e3eb 100644 --- a/packages/web-components/src/components/my-button.tsx +++ b/packages/web-components/src/components/my-button.tsx @@ -1,4 +1,5 @@ import React from 'react' +import type { TextButtonProps } from '@packages/shared' import { cn } from '../lib/utils' interface MyButtonProps extends React.ButtonHTMLAttributes { @@ -43,9 +44,7 @@ export function MyButton({ ) } -interface pButtonProps extends Omit { - text: string -} +interface pButtonProps extends Omit, TextButtonProps {} export function pButton({ text, ...props }: pButtonProps) { return {text} diff --git a/packages/web-components/src/components/quantifier.tsx b/packages/web-components/src/components/quantifier.tsx index 904bf53..c9ca3c6 100644 --- a/packages/web-components/src/components/quantifier.tsx +++ b/packages/web-components/src/components/quantifier.tsx @@ -1,16 +1,9 @@ import React from 'react' import { cn } from '../lib/utils' import { p } from './my-text' +import type { QuantifierProps } from '@packages/shared' import { Minus, Plus } from 'lucide-react' -interface QuantifierProps { - value: number - setValue: (value: number) => void - step?: number - unit?: string | { shortNotation: string } - min?: number - max?: number -} export function Quantifier({ value, diff --git a/packages/web-components/tsconfig.json b/packages/web-components/tsconfig.json index 00ff22b..abc53d9 100644 --- a/packages/web-components/tsconfig.json +++ b/packages/web-components/tsconfig.json @@ -14,7 +14,9 @@ "outDir": "./dist", "rootDir": "./src", "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@packages/shared": ["../shared"], + "@packages/shared/*": ["../shared/*"] } }, "include": ["src/**/*.ts", "src/**/*.tsx"],