enh
This commit is contained in:
parent
cfb84aae7a
commit
7ad1b81356
54 changed files with 261 additions and 692 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -132,3 +132,4 @@ dist
|
|||
.pnp.*
|
||||
|
||||
|
||||
type-clusters/*
|
||||
|
|
@ -15,15 +15,7 @@ import MaterialIcons from "@expo/vector-icons/MaterialIcons";
|
|||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import dayjs from "dayjs";
|
||||
import { useRouter } from "expo-router";
|
||||
|
||||
interface ProductGroup {
|
||||
id: number;
|
||||
groupName: string;
|
||||
description: string | null;
|
||||
createdAt: string;
|
||||
products: any[];
|
||||
productCount: number;
|
||||
}
|
||||
import type { ProductGroup } from "@/components/ProductGroupForm";
|
||||
|
||||
const GroupItem = ({
|
||||
group,
|
||||
|
|
|
|||
|
|
@ -5,14 +5,7 @@ import { AppContainer, MyText, tw, type ImageUploaderNeoItem } from 'common-ui';
|
|||
import TagForm from '@/src/components/TagForm';
|
||||
import { trpc } from '@/src/trpc-client';
|
||||
import { useUploadToObjectStorage } from '@/hooks/useUploadToObjectStore';
|
||||
|
||||
interface TagFormData {
|
||||
tagName: string;
|
||||
tagDescription: string;
|
||||
isDashboardTag: boolean;
|
||||
relatedStores: number[];
|
||||
productIds: number[];
|
||||
}
|
||||
import type { TagFormData } from '@/src/components/TagForm';
|
||||
|
||||
export default function AddTag() {
|
||||
const router = useRouter();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import ProductsSelector from './ProductsSelector';
|
|||
import { trpc } from '../src/trpc-client';
|
||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||
|
||||
interface ProductGroup {
|
||||
export interface ProductGroup {
|
||||
id: number;
|
||||
groupName: string;
|
||||
description: string | null;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import type { IdName } from '@packages/shared';
|
|||
|
||||
type StoreOption = IdName;
|
||||
|
||||
interface TagFormData {
|
||||
export interface TagFormData {
|
||||
tagName: string;
|
||||
tagDescription: string;
|
||||
isDashboardTag: boolean;
|
||||
|
|
|
|||
|
|
@ -25,21 +25,10 @@ import type {
|
|||
UserOtpVerifyResponse,
|
||||
UserPasswordUpdateResponse,
|
||||
UserDeleteAccountResponse,
|
||||
LoginRequest,
|
||||
RegisterRequest,
|
||||
} from '@packages/shared'
|
||||
|
||||
interface LoginRequest {
|
||||
identifier: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface RegisterRequest {
|
||||
name: string;
|
||||
email: string;
|
||||
mobile: string;
|
||||
password: string;
|
||||
profileImageUrl?: string | null;
|
||||
}
|
||||
|
||||
const generateToken = async (userId: number): Promise<string> => {
|
||||
return await new SignJWT({ userId })
|
||||
.setProtectedHeader({ alg: 'HS256' })
|
||||
|
|
|
|||
|
|
@ -3,12 +3,9 @@ import { useNavigate } from '@tanstack/react-router'
|
|||
import { trpc } from '@/trpc/client'
|
||||
import { useUserStore } from '@/stores/userStore'
|
||||
import { getCurrentUserId } from '@/services/auth'
|
||||
import type { ReactParentComponent } from '@packages/shared'
|
||||
|
||||
interface AuthWrapperProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export function AuthWrapper({ children }: AuthWrapperProps) {
|
||||
export function AuthWrapper({ children }: ReactParentComponent) {
|
||||
const navigate = useNavigate()
|
||||
const { setUser, setLoading, setError, user, isLoading } = useUserStore()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { useUserStore } from '@/stores/userStore';
|
||||
import type { ReactParentComponent } from '@packages/shared';
|
||||
|
||||
interface SuperAdminGuardProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function SuperAdminGuard({ children }: SuperAdminGuardProps) {
|
||||
export function SuperAdminGuard({ children }: ReactParentComponent) {
|
||||
const { user } = useUserStore();
|
||||
const allowedRoles = ['super_admin']; // Only super_admin role can access super admin features
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@
|
|||
],
|
||||
"@/src/*": [
|
||||
"../backend/src/*"
|
||||
],
|
||||
"@packages/shared": [
|
||||
"../../packages/shared"
|
||||
],
|
||||
"@packages/shared/*": [
|
||||
"../../packages/shared/*"
|
||||
]
|
||||
},
|
||||
"types": [
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import CartPage from '@/components/cart-page'
|
||||
import React from 'react'
|
||||
|
||||
interface Props {}
|
||||
|
||||
function Cart(props: Props) {
|
||||
const {} = props
|
||||
function Cart() {
|
||||
|
||||
return (
|
||||
<CartPage />
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import CheckoutPage from '@/components/checkout-page';
|
||||
import React from 'react';
|
||||
|
||||
interface Props {}
|
||||
|
||||
function Checkout(props: Props) {
|
||||
const {} = props;
|
||||
function Checkout() {
|
||||
|
||||
return <CheckoutPage />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,12 +325,7 @@ const TagScene = memo(({
|
|||
);
|
||||
});
|
||||
|
||||
interface ExploreProductItemProps {
|
||||
item: any;
|
||||
onPress: (id: number) => void;
|
||||
}
|
||||
|
||||
const ExploreProductItem = memo(({ item, onPress }: ExploreProductItemProps) => {
|
||||
const ExploreProductItem = memo(({ item, onPress }: ProductItemProps) => {
|
||||
const handlePress = useCallback(() => onPress(item.id), [item.id, onPress]);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@ import { trpc } from '@/src/trpc-client';
|
|||
import { useGetEssentialConsts } from '@/src/hooks/prominent-api-hooks';
|
||||
import dayjs from 'dayjs';
|
||||
import { useRouter } from 'expo-router';
|
||||
|
||||
interface ComplaintItemProps {
|
||||
item: any;
|
||||
}
|
||||
import type { ComplaintItemProps } from '@packages/shared';
|
||||
|
||||
const ComplaintItem: React.FC<ComplaintItemProps> = ({ item }) => (
|
||||
<View style={tw`bg-white rounded-2xl p-5 mb-4 shadow-sm border border-gray-100`}>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
MyTouchableOpacity,
|
||||
} from "common-ui";
|
||||
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
|
||||
import type { OffersSectionProps } from '@packages/shared';
|
||||
import ProductCard from "@/components/ProductCard";
|
||||
import { useAllProducts } from "@/src/hooks/prominent-api-hooks";
|
||||
import FloatingCartBar from "@/components/floating-cart-bar";
|
||||
|
|
@ -20,15 +21,6 @@ const { width: screenWidth } = Dimensions.get("window");
|
|||
const heroItemWidth = (screenWidth - 72) / 3;
|
||||
const SHOW_MORE_STEP = 6;
|
||||
|
||||
interface OffersSectionProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
products: any[];
|
||||
expanded: boolean;
|
||||
onExpand: () => void;
|
||||
onProductPress: (id: number) => void;
|
||||
}
|
||||
|
||||
// Grid section (home page style): shows the first SHOW_MORE_STEP products
|
||||
// with a "Show More" button to reveal the rest.
|
||||
const OffersSection = ({
|
||||
|
|
|
|||
|
|
@ -4,12 +4,7 @@ import { MaterialIcons } from '@expo/vector-icons'
|
|||
import { MyText, ImageUploaderNeo, tw, MyTouchableOpacity, type ImageUploaderNeoItem, type ImageUploaderNeoPayload } from 'common-ui'
|
||||
import { trpc } from '@/src/trpc-client'
|
||||
import { useUploadToObjectStorage } from '../hooks/useUploadToObjectStore'
|
||||
|
||||
interface ComplaintFormProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
orderId: number;
|
||||
}
|
||||
import type { ComplaintFormProps } from '@packages/shared'
|
||||
|
||||
export default function ComplaintForm({ open, onClose, orderId }: ComplaintFormProps) {
|
||||
const [complaintBody, setComplaintBody] = useState('');
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import { View } from 'react-native'
|
||||
import { MyButton, StorageServiceCasual, tw, MyText } from 'common-ui'
|
||||
import type { ReactParentComponent } from '@packages/shared'
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const FirstUserWrapper: React.FC<Props> = ({ children }) => {
|
||||
const FirstUserWrapper: React.FC<ReactParentComponent> = ({ children }) => {
|
||||
const [isFirstTime, setIsFirstTime] = useState<boolean | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -5,12 +5,9 @@ import { trpc, trpcClient } from '@/src/trpc-client';
|
|||
import { useGetEssentialConsts } from '@/src/hooks/prominent-api-hooks';
|
||||
import Constants from 'expo-constants';
|
||||
import * as Linking from 'expo-linking';
|
||||
import type { ReactParentComponent } from '@packages/shared';
|
||||
|
||||
interface HealthTestWrapperProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const HealthTestWrapper: React.FC<HealthTestWrapperProps> = ({ children }) => {
|
||||
const HealthTestWrapper: React.FC<ReactParentComponent> = ({ children }) => {
|
||||
const { data, isLoading, error, refetch } = trpc.common.healthCheck.useQuery();
|
||||
const { data: backendConsts } = useGetEssentialConsts();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import React, { useState, useEffect, ReactNode } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import * as Location from 'expo-location';
|
||||
import { BackHandler, Platform } from 'react-native';
|
||||
import { ConfirmationDialog } from 'common-ui';
|
||||
import type { ReactParentComponent } from '@packages/shared';
|
||||
import { trpc } from '../src/trpc-client';
|
||||
|
||||
// Emulator detection utility
|
||||
|
|
@ -41,14 +42,10 @@ const isEmulator = (): boolean => {
|
|||
}
|
||||
};
|
||||
|
||||
interface LocationTestWrapperProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
// Feature flag to enable/disable location warning dialogs
|
||||
const ENABLE_LOCATION_WARNINGS = false;
|
||||
|
||||
const LocationTestWrapper: React.FC<LocationTestWrapperProps> = ({ children }) => {
|
||||
const LocationTestWrapper: React.FC<ReactParentComponent> = ({ children }) => {
|
||||
// Skip location checks entirely for emulators
|
||||
if (isEmulator()) {
|
||||
return <>{children}</>;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import * as Updates from 'expo-updates';
|
||||
import { Alert } from 'react-native';
|
||||
import type { ReactParentComponent } from '@packages/shared';
|
||||
|
||||
interface UpdateCheckerProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const UpdateChecker: React.FC<UpdateCheckerProps> = ({ children }) => {
|
||||
const UpdateChecker: React.FC<ReactParentComponent> = ({ children }) => {
|
||||
useEffect(() => {
|
||||
const checkForUpdates = async () => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -5,12 +5,9 @@ import { trpc } from '@/src/trpc-client';
|
|||
import { useGetEssentialConsts } from '@/src/hooks/prominent-api-hooks';
|
||||
import { MyTouchableOpacity } from 'common-ui';
|
||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||
import type { ReactParentComponent } from '@packages/shared';
|
||||
|
||||
interface WebViewWrapperProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function WebViewWrapper({ children }: WebViewWrapperProps) {
|
||||
export default function WebViewWrapper({ children }: ReactParentComponent) {
|
||||
const { data: constsData } = useGetEssentialConsts();
|
||||
const [isClosed, setIsClosed] = useState(false);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import React, {
|
|||
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";
|
||||
import type { ReactParentComponent } from '@packages/shared';
|
||||
|
||||
interface NotificationContextType {
|
||||
expoPushToken: string | null;
|
||||
|
|
@ -33,11 +33,7 @@ export const useNotification = () => {
|
|||
return context;
|
||||
};
|
||||
|
||||
interface NotificationProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const NotificationProvider: React.FC<NotificationProviderProps> = ({
|
||||
export const NotificationProvider: React.FC<ReactParentComponent> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [expoPushToken, setExpoPushToken] = useState<string | null>(null);
|
||||
|
|
|
|||
|
|
@ -7,25 +7,7 @@ import { tw, MyText, MyTouchableOpacity , Checkbox , MyTextInput , LoadingDialog
|
|||
import { trpc } from '../trpc-client';
|
||||
import LocationAttacher from './LocationAttacher';
|
||||
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
|
||||
|
||||
interface AddressFormProps {
|
||||
onSuccess: (addressId?: number) => void;
|
||||
initialValues?: {
|
||||
id?: number;
|
||||
name: string;
|
||||
phone: string;
|
||||
addressLine1: string;
|
||||
addressLine2: string;
|
||||
city: string;
|
||||
state: string;
|
||||
pincode: string;
|
||||
isDefault: boolean;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
googleMapsUrl?: string;
|
||||
};
|
||||
isEdit?: boolean;
|
||||
}
|
||||
import type { AddressFormProps } from '@packages/shared';
|
||||
|
||||
const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isEdit = false }) => {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import React from 'react';
|
||||
import { useInitializeCentralSlotStore } from '@/src/store/centralSlotStore';
|
||||
import { useInitializeCentralProductStore } from '@/src/store/centralProductStore';
|
||||
import type { ReactParentComponent } from '@packages/shared';
|
||||
|
||||
interface CentralStoreInitializerProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function CentralStoreInitializer({ children }: CentralStoreInitializerProps) {
|
||||
export default function CentralStoreInitializer({ children }: ReactParentComponent) {
|
||||
useInitializeCentralSlotStore();
|
||||
useInitializeCentralProductStore();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||
import { getAuthToken, saveAuthToken, deleteAuthToken, saveUserId, getUserId } from '../../hooks/useJWT';
|
||||
import { getCurrentUserId } from '@/utils/getCurrentUserId';
|
||||
import { useRegister } from '@/src/api-hooks/auth.api';
|
||||
|
|
@ -7,6 +7,7 @@ import { trpc } from '@/src/trpc-client';
|
|||
import { StorageServiceCasual } from 'common-ui';
|
||||
import { useRouter } from 'expo-router';
|
||||
import constants from '@/src/constants';
|
||||
import type { ReactParentComponent } from '@packages/shared';
|
||||
|
||||
export interface RedirectState {
|
||||
targetUrl: string;
|
||||
|
|
@ -16,11 +17,7 @@ export interface RedirectState {
|
|||
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||
|
||||
interface AuthProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||
export const AuthProvider: React.FC<ReactParentComponent> = ({ children }) => {
|
||||
const router = useRouter();
|
||||
const [authState, setAuthState] = useState<AuthState>({
|
||||
user: null,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
|
||||
interface AddedToCartProduct {
|
||||
productId: number;
|
||||
product: any;
|
||||
}
|
||||
|
||||
interface CartStore {
|
||||
addedToCartProduct: AddedToCartProduct | null;
|
||||
setAddedToCartProduct: (product: AddedToCartProduct | null) => void;
|
||||
clearAddedToCartProduct: () => void;
|
||||
}
|
||||
import type { AddedToCartProduct, CartStore } from '@packages/shared';
|
||||
|
||||
export const useCartStore = create<CartStore>((set) => ({
|
||||
addedToCartProduct: null,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { create } from 'zustand';
|
||||
import type { AddedToCartProduct } from '@packages/shared';
|
||||
|
||||
interface AddedFlashProduct {
|
||||
productId: number;
|
||||
product: any;
|
||||
}
|
||||
type AddedFlashProduct = AddedToCartProduct;
|
||||
|
||||
interface FlashCartStore {
|
||||
addedFlashProduct: AddedFlashProduct | null;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
|
||||
interface StoreHeaderState {
|
||||
title: string;
|
||||
setTitle: (title: string) => void;
|
||||
}
|
||||
import type { StoreHeaderState } from '@packages/shared';
|
||||
|
||||
export const useStoreHeaderStore = create<StoreHeaderState>((set) => ({
|
||||
title: '',
|
||||
|
|
|
|||
|
|
@ -3,25 +3,7 @@ import { trpc } from '../lib/trpc-client'
|
|||
import { p, pInput, MyButton, div } from 'web-components'
|
||||
import { MapPin, X, Plus } from 'lucide-react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
interface AddressFormProps {
|
||||
onSuccess: (addressId?: number) => void
|
||||
initialValues?: {
|
||||
id?: number
|
||||
name: string
|
||||
phone: string
|
||||
addressLine1: string
|
||||
addressLine2: string
|
||||
city: string
|
||||
state: string
|
||||
pincode: string
|
||||
isDefault: boolean
|
||||
latitude?: number
|
||||
longitude?: number
|
||||
googleMapsUrl?: string
|
||||
}
|
||||
isEdit?: boolean
|
||||
}
|
||||
import type { AddressFormProps } from '@packages/shared'
|
||||
|
||||
const validationSchema = Yup.object({
|
||||
name: Yup.string().required('Name is required'),
|
||||
|
|
|
|||
|
|
@ -2,12 +2,7 @@ import { useState, useRef } from 'react'
|
|||
import { X, Upload, ImageIcon, Loader2 } from 'lucide-react'
|
||||
import { trpc } from '../lib/trpc-client'
|
||||
import { useUploadToObjectStorage } from '../hooks/useUploadToObjectStorage'
|
||||
|
||||
interface ComplaintFormProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
orderId: number
|
||||
}
|
||||
import type { ComplaintFormProps } from '@packages/shared'
|
||||
|
||||
interface ComplaintImage {
|
||||
imgUrl: string
|
||||
|
|
|
|||
|
|
@ -1,15 +1,5 @@
|
|||
import { create } from 'zustand'
|
||||
|
||||
interface AddedToCartProduct {
|
||||
productId: number
|
||||
product: any
|
||||
}
|
||||
|
||||
interface CartStore {
|
||||
addedToCartProduct: AddedToCartProduct | null
|
||||
setAddedToCartProduct: (product: AddedToCartProduct | null) => void
|
||||
clearAddedToCartProduct: () => void
|
||||
}
|
||||
import type { AddedToCartProduct, CartStore } from '@packages/shared'
|
||||
|
||||
export const useCartStore = create<CartStore>((set) => ({
|
||||
addedToCartProduct: null,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { create } from 'zustand'
|
||||
import type { StoreHeaderState } from '@packages/shared'
|
||||
|
||||
interface StoreHeaderStore {
|
||||
title: string
|
||||
setTitle: (title: string) => void
|
||||
}
|
||||
type StoreHeaderStore = StoreHeaderState
|
||||
|
||||
export const useStoreHeaderStore = create<StoreHeaderStore>((set) => ({
|
||||
title: '',
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { usePopulateCentralStores } from '../hooks/usePopulateCentralStores'
|
|||
import { usePopulateCentralProductStore } from '../hooks/usePopulateCentralProductStore'
|
||||
import { AppLayout } from '../components/AppLayout'
|
||||
import { Zap, ShoppingCart } from 'lucide-react'
|
||||
import type { CompactProductCardProps } from '@packages/shared'
|
||||
|
||||
export const Route = createFileRoute('/flash')({
|
||||
component: FlashDeliveryPage,
|
||||
|
|
@ -203,12 +204,6 @@ const formatQuantity = (
|
|||
return { value: `${quantity} ${unit}(s)`, display: `${quantity}${unit}` };
|
||||
};
|
||||
|
||||
interface CompactProductCardProps {
|
||||
item: any;
|
||||
handleAddToCart: (productId: number) => void;
|
||||
onPress?: () => void;
|
||||
}
|
||||
|
||||
function CompactProductCard({
|
||||
item,
|
||||
handleAddToCart,
|
||||
|
|
|
|||
|
|
@ -6,13 +6,10 @@ import { useGetEssentialConsts } from '../hooks/prominent-api-hooks'
|
|||
import dayjs from 'dayjs'
|
||||
import { Dialog } from '../components/Dialog'
|
||||
import { useState } from 'react'
|
||||
import type { ComplaintItemProps } from '@packages/shared'
|
||||
|
||||
export const Route = createFileRoute('/me/complaints')({ component: ComplaintsPage })
|
||||
|
||||
interface ComplaintItemProps {
|
||||
item: any
|
||||
}
|
||||
|
||||
function ComplaintItem({ item }: ComplaintItemProps) {
|
||||
return (
|
||||
<div className="mb-4 rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
|
||||
|
|
|
|||
|
|
@ -4,20 +4,12 @@ import { trpc } from '../lib/trpc-client'
|
|||
import { Tag, Loader2, AlertCircle } from 'lucide-react'
|
||||
import { ProductCard } from '../components/ProductCard'
|
||||
import { AppLayout } from '../components/AppLayout'
|
||||
import type { OffersSectionProps } from '@packages/shared'
|
||||
|
||||
export const Route = createFileRoute('/offers')({ component: OffersPage })
|
||||
|
||||
const SHOW_MORE_STEP = 6
|
||||
|
||||
interface OffersSectionProps {
|
||||
title: string
|
||||
subtitle: string
|
||||
products: any[]
|
||||
expanded: boolean
|
||||
onExpand: () => void
|
||||
onProductPress: (id: number) => void
|
||||
}
|
||||
|
||||
function OffersSection({ title, subtitle, products, expanded, onExpand, onProductPress }: OffersSectionProps) {
|
||||
const visible = expanded ? products : products.slice(0, SHOW_MORE_STEP)
|
||||
const hasMore = products.length > SHOW_MORE_STEP && !expanded
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import { usePopulateCentralProductStore } from "../hooks/usePopulateCentralProdu
|
|||
import { AppLayout } from "../components/AppLayout";
|
||||
import { Truck, Store, Grid3X3, ChevronLeft, ShoppingCart, Clock, ChevronDown } from "lucide-react";
|
||||
import { Dialog } from "../components/Dialog";
|
||||
import type { CompactProductCardProps } from '@packages/shared';
|
||||
|
||||
export const Route = createFileRoute("/slot-view")({
|
||||
component: SlotViewPage,
|
||||
|
|
@ -397,12 +398,6 @@ const formatQuantity = (
|
|||
return { value: `${quantity} ${unit}(s)`, display: `${quantity}${unit}` };
|
||||
};
|
||||
|
||||
interface CompactProductCardProps {
|
||||
item: any;
|
||||
handleAddToCart: (productId: number) => void;
|
||||
onPress?: () => void;
|
||||
}
|
||||
|
||||
function CompactProductCard({
|
||||
item,
|
||||
handleAddToCart,
|
||||
|
|
|
|||
130
change-log.txt
130
change-log.txt
|
|
@ -854,3 +854,133 @@ Centralized in @packages/shared and re-used:
|
|||
- Fixed as found: app-common.types.ts added to shared index star-exports; AvailabilityEntry export added in user-ui hooks; 3 dangling store-helpers re-exports repaired
|
||||
Verification: backend 11, sqlite 4, user-ui 106, admin-ui 122 (all = baselines); pg 45 = 40 + 5 pg-coupon casts that reproduce with byte-restored admin.ts (pre-existing drift from prior session, documented in report §8). Zero errors mention any unified name.
|
||||
Deliberately NOT unified (documented in repeat_types_glm.md §8 + change-log): drifted input types (coupon/tag/slot/product), InferSelectModel *Row aliases (schema-bound), ui<->web-components (no cross-dep), migrator ColumnInfo (field names differ per direction), CartData (element types differ), backend banner-store Banner (subset shape), const-keys files (different key sets).
|
||||
|
||||
[2026-09-05 00:24:25] PHASE 5: unify remaining verified exact-duplicate types.
|
||||
- shared primitives.types.ts: added BasicSuccessResponse {success: boolean}
|
||||
- shared admin.ts: AdminDeleteProductResult, AdminSlotDeleteResult → aliases of MessageResponse; AdminSlotUpdateResult → alias of AdminSlotCreateResult
|
||||
- shared user.ts: UserSavePushTokenResponse → alias of BasicSuccessResponse; UserDeliverySlot → alias of AdminDeliverySlot (+import from ./admin; no cycle — admin.ts does not import user.ts)
|
||||
- shared app-common.types.ts: DELETED zero-consumer stale CartData (wrong element type), DropdownOption, CartIconProps; REPLACED stale AddressFormProps with the real current shape; ADDED StoreHeaderState, CartStore, AddedToCartProduct, CompactProductCardProps, ComplaintItemProps
|
||||
- admin-ui TagForm.tsx: exported TagFormData; product-tags/add.tsx: deleted local, imports it (edit/index.tsx extended version untouched)
|
||||
- admin-ui ProductGroupForm.tsx: exported ProductGroup; product-groupings/index.tsx: deleted local, imports it
|
||||
- user-ui + web-ui AddressForm: deleted locals, import shared AddressFormProps
|
||||
- user-ui + web-ui ComplaintForm: deleted locals, import shared ComplaintFormProps
|
||||
- web-ui flash.tsx + slot-view.tsx: deleted local CompactProductCardProps, import shared
|
||||
- user-ui complaints/index.tsx + web-ui me.complaints.tsx: deleted local ComplaintItemProps, import shared
|
||||
- user-ui cartStore + web-ui cart-store: deleted local AddedToCartProduct + CartStore, import shared; flashCartStore: AddedFlashProduct → alias of shared AddedToCartProduct
|
||||
- user-ui storeHeaderStore + web-ui store-header-store: deleted local state interfaces, import shared StoreHeaderState; web-ui keeps hook name via type StoreHeaderStore = StoreHeaderState
|
||||
- user-ui home/index.tsx: deleted ExploreProductItemProps, uses ProductItemProps
|
||||
- user-ui cart.tsx + checkout.tsx: deleted empty Props{} + dropped unused props params
|
||||
- backend order.ts: deleted second DeliveryStatus/OrderStatus pair (lines ~477-478), first pair serves both scopes
|
||||
NOT unified (verified different or out of scope, documented): ChipProps/TagChipProps (different Tag element types), Tag pair (productIds? vs imageUrl), ProductsResponse family (backend-circular), CartData element types, StoreHeaderSt— n/a, migrator ColumnInfo, ui<->web props, fallback-ui 3d props, ProductsResponseAlias/ProductsResponse placeholders.
|
||||
|
||||
[2026-09-05 00:30:00] CORRECTION: reverted deletion of backend order.ts second DeliveryStatus/OrderStatus pair — both pairs are function-scoped (different closures), not file duplicates. No change needed there; cluster 49/50 verdict in types_inventory was wrong on scope.
|
||||
|
||||
[2026-09-05 00:35:00] PHASE 5 (continued): unify remaining verified exact-duplicates; CORRECTIONS for two over-eager aliases.
|
||||
- shared primitives.types.ts: added BasicSuccessResponse {success: boolean}
|
||||
- shared admin.ts: AdminSlotUpdateResult → alias of AdminSlotCreateResult; AdminOrderBasicResult → alias of BasicSuccessResponse
|
||||
- shared user.ts: UserDeliverySlot → alias of AdminDeliverySlot (+import; no cycle)
|
||||
- shared app-common.types.ts rewritten: DELETED zero-consumer stale CartData (wrong element type), DropdownOption, CartIconProps, RedirectState, VendorSnippetProduct, ProductsResponseAlias/ProductsResponse placeholders; REPLACED stale AddressFormProps with real shape; ADDED StoreHeaderState, CartStore, AddedToCartProduct, CompactProductCardProps, ComplaintItemProps
|
||||
- user-ui + web-ui cart stores: local AddedToCartProduct + CartStore deleted, import shared; flashCartStore AddedFlashProduct → alias
|
||||
- user-ui storeHeaderStore + web-ui store-header-store: locals deleted, import shared StoreHeaderState (web-ui keeps name via alias)
|
||||
- user-ui + web-ui AddressForm/ComplaintForm: locals deleted, import shared
|
||||
- web-ui flash.tsx + slot-view.tsx: local CompactProductCardProps deleted, import shared
|
||||
- user-ui complaints + web-ui me.complaints: local ComplaintItemProps deleted, import shared
|
||||
- admin-ui TagForm.tsx: TagFormData exported; product-tags/add.tsx imports it (edit/index extended version untouched)
|
||||
- admin-ui ProductGroupForm.tsx: ProductGroup exported; product-groupings/index.tsx imports it
|
||||
- user-ui home/index.tsx: ExploreProductItemProps deleted (uses ProductItemProps)
|
||||
- user-ui cart.tsx + checkout.tsx: empty Props{} + unused props params deleted
|
||||
- CORRECTION 1: reverted AdminDeleteProductResult/AdminSlotDeleteResult → MessageResponse aliases — backend procedures return {message} without success; aliasing changed the contract (tsc proved it). Restored local {message} interfaces. ({success,message}×9 aliases unaffected — identical contracts.)
|
||||
- CORRECTION 2: removed accidentally-duplicated UserSavePushTokenResponse alias line in user.ts (duplicate identifier).
|
||||
- CORRECTION 3: reverted backend order.ts DeliveryStatus/OrderStatus deletion — both pairs are function-scoped, not file duplicates.
|
||||
- NOT unified (verified): ChipProps/TagChipProps (different Tag element types), Tag pair, ProductsResponse family (backend-circular), StoreHeader n/a, AdminCancelOrderError (union, not same shape), OffersSectionProps (single def), Props usage n/a.
|
||||
|
||||
[2026-09-05 00:40:00] CREATED analysis script scripts/subset-types.js (read-only subset/superset scanner; outputs scripts/subset-types-raw.json). No source changes in this entry.
|
||||
|
||||
[2026-09-05 00:47:00] PHASE 6: unify remaining verified exact-duplicates.
|
||||
- shared seed.types.ts: KeyValSeedData → alias of Constant (+import from ./const.types; no cycle — const.types has no imports)
|
||||
- shared user.ts: UserProductStoreInfo → alias of StoreSummary (+import from ./store.types; no cycle)
|
||||
- shared cache.types.ts: DELETED PlacedOrderShape (zero consumers repo-wide; db PlacedOrder in order.types.ts is canonical)
|
||||
- backend auth.ts: deleted local LoginRequest/RegisterRequest, import from '@packages/shared' (same treatment user-ui got)
|
||||
- sqlite+pg user-apis/order.ts: OrderDetailWithRelations → alias of OrderWithRelations (verified byte-identical bodies 879/879 and 820/820)
|
||||
- shared app-common.types.ts: added FlashDeliveryProps {isFlashDelivery?: boolean}
|
||||
- user-ui cart-page/checkout-page + web-ui FloatingCartBar: {isFlashDelivery?} prop interfaces → aliases of shared FlashDeliveryProps
|
||||
- user-ui home/index.tsx: deleted RenderStoreProps, uses SlotItemProps (same file, identical)
|
||||
- web-components image-gallery.tsx: ImageGalleryProps → alias of ImageCarouselProps (same package)
|
||||
NOT unified (verified): Tag pair user-ui/web-ui (name-collision risk with backend's richer Tag — same judgment as ChipProps pair); DropdownOption pair (ui has extra disabled?); ImageUploader/quantifier/dialog/checkbox/profile/search props (independent RN/web packages); CharactersProps (fallback-ui, out of scope); ProductsResponse family (backend-circular); migrator TableInfo (ColumnInfo differs); AndSnippets vs Base (verified DIFFERENT — WithAccess vs plain vendor snippets); SkuSummary admin-ui (sqlite's not exported via index); RegisterRequest backend done above.
|
||||
|
||||
[2026-09-05 10:01:39] COMPLETED Phase 6 second wave + full verification.
|
||||
- shared: KeyValSeedData→Constant, UserProductStoreInfo→StoreSummary, AdminOrderBasicResult+UserSavePushTokenResponse→BasicSuccessResponse (new primitive), AdminSlotUpdateResult→AdminSlotCreateResult, UserDeliverySlot→AdminDeliverySlot, AdminDeleteProductResult + AdminSlotDeleteResult restored as local {message} (contract — backend returns message-only), app-common rewritten (real AddressFormProps; added StoreHeaderState/CartStore/AddedToCartProduct/CompactProductCardProps/ComplaintItemProps; deleted stale CartData/DropdownOption/CartIconProps/RedirectState/VendorSnippetProduct/placeholders)
|
||||
- backend auth.ts: LoginRequest/RegisterRequest → shared imports
|
||||
- db packages: OrderDetailWithRelations → alias (both, byte-identical verified); PlacedOrderShape deleted (zero consumers)
|
||||
- admin-ui: TagFormData/ProductGroup canonical in form components; VendorSnippetProduct/StoreOption/Staff → IdName aliases
|
||||
- user-ui + web-ui: cart/header stores, AddressForm, ComplaintForm, CompactProductCardProps, ComplaintItemProps → shared; ExploreProductItemProps/Props{}/RenderStoreProps removed
|
||||
- web-ui store-header keeps hook name via alias
|
||||
Verification: backend 11, sqlite 4, pg 45 (40+5 pre-existing coupon casts), user-ui 106, admin-ui 122 — all baselines hold; web-ui 150 (no baseline; errors are pre-existing JSX friction, none touch unified names).
|
||||
Left deliberately (verified): Tag pair (backend name-collision risk), DropdownOption pair (ui has extra disabled?), ui<->web prop mirrors (independent packages), ChipProps pair, ProductsResponse family, migrator ColumnInfo, AndSnippets vs Base (verified DIFFERENT), fallback-ui (out of scope), drifted inputs, InferSelectModel rows, SkuSummary (not exported via index), DeliveryStatus/OrderStatus (function-scoped), OffersSectionProps (single), Props usage n/a.
|
||||
|
||||
[2026-09-05 10:10:00] Regen unification: OffersSectionProps (web-ui offers.tsx + user-ui order-again/index.tsx, verified identical) → shared app-common.types.ts; both routes import it.
|
||||
|
||||
[2026-09-05 10:20:00] Regen unification round 2: OffersSectionProps → shared app-common (web-ui offers + user-ui order-again import it); ImageGalleryProps → alias of ImageCarouselProps (same package web-components).
|
||||
|
||||
[2026-09-05 10:22:00] web-components image-carousel.tsx: exported ImageCarouselProps (needed by image-gallery.tsx alias).
|
||||
|
||||
[2026-09-05 10:30:00] Regen catch: packages/shared/types/cache.types.ts declared ProductTagData TWICE (bad merge) — deleted the duplicate second copy. No consumers affected (same shape).
|
||||
|
||||
[2026-09-05 10:35:00] REGENERATED types_inventory.md via new scripts/gen-inventory.js + scripts/render-inventory.js (read-only analysis scripts).
|
||||
- Fresh harvest: 562 files, 620 declarations, 490 unique names, 42 single-identifier aliases (classified separately), 98 duplicate names, 56 clusters, 698 any-hits.
|
||||
- Harvest fixes vs 09-03 edition: import/export-block lines never harvested (~17 bogus clusters gone); aliases excluded from shape clustering (new Unified Aliases section); React. qualifier stripped; optionality ignored (documented).
|
||||
- Fixed as found during regen: duplicate ProductTagData in shared cache.types.ts (deleted 2nd copy); ImageGalleryProps → alias of ImageCarouselProps (+export added); OffersSectionProps → shared app-common (web-ui + user-ui import it).
|
||||
- Verification: backend 11, sqlite 4, user-ui 106, admin-ui 122, pg 45 (40+5 pre-existing) — all baselines hold; web-components 3 pre-existing JSX errors, untouched by edits.
|
||||
|
||||
[2026-09-05 10:45:00] CREATED analysis scripts (read-only): scripts/gen-inventory.js (types harvest + clustering), scripts/render-inventory.js (md renderer with per-cluster verdicts), scripts/gen-cluster-files.js (per-cluster md files with full defs + consumers), scripts/update-inventory.js (one-off patch helper for the 09-03 edition; superseded by regeneration).
|
||||
GENERATED: type-clusters/ folder (56 cluster files + README index).
|
||||
REGENERATED: types_inventory.md from current tree (replaces 09-03 snapshot + manual annotations).
|
||||
|
||||
[2026-09-05 10:57:13] DELETE all agent-created analysis scripts (user request — remove, not needed; nothing references them in code or configs).
|
||||
Deleted files in scripts/:
|
||||
- gen-cluster-files.js, gen-inventory.js, render-inventory.js, update-inventory.js
|
||||
- subset-types.js, subset-types-raw.json, types-inventory-data.json
|
||||
Deleted directory scripts/dead-code/ entirely (contained only agent-created files):
|
||||
- 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
|
||||
KEPT (pre-existing, not mine): scripts/clear-db-old.sql, scripts/clear-db.sql, scripts/s3-cleaner.js, scripts/s3-sync.js
|
||||
Follow-up md-only cleanup: subset_types_glm.md lines referencing the deleted scripts reworded (no code impact).
|
||||
|
||||
[2026-09-05 11:03:50] ADD shared ReactParentComponent for Cluster-1 unification.
|
||||
=== packages/shared/types/primitives.types.ts ===
|
||||
- added type-only react import + new interface (appended):
|
||||
import type { ReactNode } from 'react'
|
||||
export interface ReactParentComponent {
|
||||
children: ReactNode
|
||||
}
|
||||
- header comment updated: leaf module rule now reads "no runtime imports (type-only imports erased at compile are fine)".
|
||||
|
||||
[2026-09-05 11:06:00] Cluster-1 unification: replace 9 local children-only interfaces with shared ReactParentComponent (user-ui + ui).
|
||||
- LocationTestWrapper.tsx: delete lines 44-46 (interface LocationTestWrapperProps); add shared type import; `: LocationTestWrapperProps` → `: ReactParentComponent` at use site line 51
|
||||
- HealthTestWrapper.tsx: delete lines 9-11 (interface HealthTestWrapperProps); add import; `React.FC<HealthTestWrapperProps>` → `React.FC<ReactParentComponent>`
|
||||
- WebViewWrapper.tsx: delete lines 9-11; add import; `{ children }: WebViewWrapperProps` → `{ children }: ReactParentComponent`
|
||||
- FirstUserWrapper.tsx: delete lines 5-7 (interface Props); add import; `React.FC<Props>` → `React.FC<ReactParentComponent>`
|
||||
- UpdateChecker.tsx: delete lines 5-7; add import; `React.FC<UpdateCheckerProps>` → `React.FC<ReactParentComponent>`
|
||||
- notif-context.tsx: delete lines 36-38; add import; `React.FC<NotificationProviderProps>` → `React.FC<ReactParentComponent>`
|
||||
- AuthContext.tsx: delete lines 19-21; add import; `React.FC<AuthProviderProps>` → `React.FC<ReactParentComponent>`
|
||||
- CentralStoreInitializer.tsx: delete lines 5-7; add import; `{ children }: CentralStoreInitializerProps` → `{ children }: ReactParentComponent`
|
||||
- ui app-container.tsx: delete lines 7-9 (interface Props); add import; `(props: Props)` → `(props: ReactParentComponent)`
|
||||
Import line used everywhere: import type { ReactParentComponent } from '@packages/shared'
|
||||
Excluded: MyTouchableOpacityProps (optional children + extends — different shape); other files' same-named Props (different interfaces, verified).
|
||||
|
||||
[2026-09-05 11:10:00] CORRECTION (LocationTestWrapper): my first edit inserted a duplicate component declaration (interface did not directly precede the component). Repaired — file now has single component with ReactParentComponent annotation, interface + comment preserved correctly.
|
||||
|
||||
[2026-09-05 11:07:29] Cluster-1 fallback-ui: add @packages/shared path mapping to tsconfig (mirrors user-ui mapping), replace SuperAdminGuardProps + AuthWrapperProps with shared ReactParentComponent.
|
||||
- apps/fallback-ui/tsconfig.json paths: added "@packages/shared": ["../../packages/shared"], "@packages/shared/*": ["../../packages/shared/*"]
|
||||
- SuperAdminGuard.tsx: deleted lines 3-5 interface; added shared type import; `{ children }: SuperAdminGuardProps` → `{ children }: ReactParentComponent`
|
||||
- AuthWrapper.tsx: deleted lines 7-9 interface; added shared type import; `{ children }: AuthWrapperProps` → `{ children }: ReactParentComponent`
|
||||
|
||||
[2026-09-05 11:08:15] Cluster-1 cleanup: remove now-unused ReactNode named imports (orphaned by interface deletions).
|
||||
- notif-context.tsx: removed `ReactNode,` from react import block
|
||||
- AuthContext.tsx: removed `ReactNode` from react import list
|
||||
|
||||
[2026-09-05 11:09:00] Cluster-1 cleanup: remove orphaned ReactNode named import in LocationTestWrapper.tsx (only remaining ReactNode use was the deleted interface).
|
||||
|
||||
[2026-09-05 11:12:00] FIX: shared/types/index.ts never starred primitives.types (existing MessageResponse/IdName only surfaced via admin.ts re-export). Added export type * from './primitives.types'; so ReactParentComponent resolves from '@packages/shared'.
|
||||
|
||||
[2026-09-05 11:15:00] COMPLETED Cluster-1 ReactParentComponent unification (11 files + shared def + fallback-ui tsconfig).
|
||||
Verification: user-ui 106, admin-ui 122, backend 11 (all = baselines); fallback-ui 107 total with zero errors in touched files. Zero leftover local interfaces. Docs: cluster-01 file + types_inventory status section marked resolved.
|
||||
|
|
|
|||
|
|
@ -67,50 +67,7 @@ export interface OrderWithRelations {
|
|||
}>
|
||||
}
|
||||
|
||||
export interface OrderDetailWithRelations {
|
||||
id: number
|
||||
userId: number
|
||||
addressId: number
|
||||
slotId: number | null
|
||||
totalAmount: string
|
||||
deliveryCharge: string
|
||||
isCod: boolean
|
||||
isOnlinePayment: boolean
|
||||
isFlashDelivery: boolean
|
||||
userNotes: string | null
|
||||
createdAt: Date
|
||||
orderItems: Array<{
|
||||
id: number
|
||||
productId: number
|
||||
quantity: string
|
||||
price: string
|
||||
discountedPrice: string | null
|
||||
is_packaged: boolean
|
||||
product: {
|
||||
id: number
|
||||
name: string
|
||||
images: unknown
|
||||
}
|
||||
}>
|
||||
slot: {
|
||||
deliveryTime: Date
|
||||
} | null
|
||||
paymentInfo: {
|
||||
id: number
|
||||
status: string
|
||||
} | null
|
||||
orderStatus: Array<{
|
||||
id: number
|
||||
isCancelled: boolean
|
||||
isDelivered: boolean
|
||||
paymentStatus: string
|
||||
cancelReason: string | null
|
||||
}>
|
||||
refunds: Array<{
|
||||
refundStatus: string
|
||||
refundAmount: string | null
|
||||
}>
|
||||
}
|
||||
export type OrderDetailWithRelations = OrderWithRelations;
|
||||
|
||||
export interface CouponValidationResult {
|
||||
id: number
|
||||
|
|
|
|||
|
|
@ -74,53 +74,7 @@ export interface OrderWithRelations {
|
|||
}>
|
||||
}
|
||||
|
||||
export interface OrderDetailWithRelations {
|
||||
id: number
|
||||
userId: number
|
||||
addressId: number
|
||||
slotId: number | null
|
||||
totalAmount: string
|
||||
deliveryCharge: string
|
||||
isCod: boolean
|
||||
isOnlinePayment: boolean
|
||||
isFlashDelivery: boolean
|
||||
userNotes: string | null
|
||||
createdAt: Date
|
||||
orderItems: Array<{
|
||||
id: number
|
||||
skuId: number
|
||||
quantity: string
|
||||
price: string
|
||||
discountedPrice: string | null
|
||||
is_packaged: boolean
|
||||
sku: {
|
||||
id: number
|
||||
name: string | null
|
||||
images: unknown
|
||||
product: {
|
||||
name: string
|
||||
} | null
|
||||
} | null
|
||||
}>
|
||||
slot: {
|
||||
deliveryTime: Date
|
||||
} | null
|
||||
paymentInfo: {
|
||||
id: number
|
||||
status: string
|
||||
} | null
|
||||
orderStatus: Array<{
|
||||
id: number
|
||||
isCancelled: boolean
|
||||
isDelivered: boolean
|
||||
paymentStatus: string
|
||||
cancelReason: string | null
|
||||
}>
|
||||
refunds: Array<{
|
||||
refundStatus: string
|
||||
refundAmount: string | null
|
||||
}>
|
||||
}
|
||||
export type OrderDetailWithRelations = OrderWithRelations;
|
||||
|
||||
export interface CouponValidationResult {
|
||||
id: number
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// (Entity shapes live in the dedicated per-domain files — re-exported here
|
||||
// so existing `@packages/shared` consumers keep working from one surface.)
|
||||
|
||||
import type { MessageResponse, IdName } from './primitives.types'
|
||||
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'
|
||||
|
|
@ -189,9 +189,7 @@ export interface AdminOrderItemPackagingResult {
|
|||
|
||||
export type AdminOrderMessageResult = MessageResponse
|
||||
|
||||
export interface AdminOrderBasicResult {
|
||||
success: boolean;
|
||||
}
|
||||
export type AdminOrderBasicResult = BasicSuccessResponse
|
||||
|
||||
export interface AdminSlotOrderItem {
|
||||
id: number;
|
||||
|
|
@ -595,11 +593,7 @@ export interface AdminSlotCreateResult {
|
|||
message: string;
|
||||
}
|
||||
|
||||
export interface AdminSlotUpdateResult {
|
||||
slot: AdminDeliverySlot;
|
||||
createdSnippets: AdminVendorSnippet[];
|
||||
message: string;
|
||||
}
|
||||
export type AdminSlotUpdateResult = AdminSlotCreateResult
|
||||
|
||||
export interface AdminSlotDeleteResult {
|
||||
message: string;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,9 @@
|
|||
/**
|
||||
* App-Common Types
|
||||
* Duplicated identically across apps (admin-ui / user-ui / web-ui / ui).
|
||||
* Duplicated identically across apps (admin-ui / user-ui / web-ui).
|
||||
* Kept in @packages/shared so all apps import one source.
|
||||
*/
|
||||
|
||||
// --- Upload / prominent hooks (already in upload.types.ts, but also include the indexed aliases) ---
|
||||
// AvailabilityEntry is `AvailabilityApiType['availability'][number]` — not shareable generically;
|
||||
// apps keep that local. The truly shared hook aliases are the response type aliases:
|
||||
export type ProductsResponseAlias = import('./user').UserProductDetailData
|
||||
// For the literal alias trio that were copy-pasted across user-ui ↔ web-ui:
|
||||
export type ProductsResponse = import('./user').UserProductDetailData // placeholder — apps re-export via AllProductsApiType
|
||||
|
||||
// --- Forms / shared UI ---
|
||||
export interface LoginFormInputs {
|
||||
mobile: string
|
||||
|
|
@ -24,62 +17,62 @@ export interface ComplaintFormProps {
|
|||
orderId: number
|
||||
}
|
||||
|
||||
// Duplicate across user-ui hooks ↔ web-ui hooks
|
||||
export interface CartData {
|
||||
items: import('./user').CartItem[]
|
||||
totalAmount: number
|
||||
totalItems: number
|
||||
}
|
||||
|
||||
// Cart icon props duplicated in user-ui ↔ ui
|
||||
export interface CartIconProps {
|
||||
color: string
|
||||
focused: boolean
|
||||
size: number
|
||||
}
|
||||
|
||||
// Address form props duplicated user-ui ↔ web-ui
|
||||
// Address form props — identical in user-ui + web-ui AddressForm components
|
||||
export interface AddressFormProps {
|
||||
addressLine1: string
|
||||
addressLine2: string
|
||||
city: string
|
||||
googleMapsUrl?: string
|
||||
id?: number
|
||||
onSuccess: (addressId?: number) => void
|
||||
initialValues?: {
|
||||
id?: number
|
||||
name: string
|
||||
phone: string
|
||||
addressLine1: string
|
||||
addressLine2: string
|
||||
city: string
|
||||
state: string
|
||||
pincode: string
|
||||
isDefault: boolean
|
||||
latitude?: number
|
||||
longitude?: number
|
||||
googleMapsUrl?: string
|
||||
}
|
||||
isDefault: boolean
|
||||
isEdit?: boolean
|
||||
}
|
||||
|
||||
// Shared UI dropdown — duplicated ui ↔ web-components
|
||||
export interface DropdownOption {
|
||||
label: string
|
||||
value: string | number
|
||||
// Complaint item — identical in user-ui + web-ui complaints lists
|
||||
export interface ComplaintItemProps {
|
||||
item: any
|
||||
}
|
||||
|
||||
// ImageUploaderNeo — duplicated ui ↔ web-components
|
||||
export interface ImageUploaderNeoItem {
|
||||
imgUrl: string
|
||||
mimeType: string | null
|
||||
// Compact product card — identical in web-ui flash.tsx + slot-view.tsx
|
||||
export interface CompactProductCardProps {
|
||||
item: any
|
||||
handleAddToCart: (productId: number) => void
|
||||
onPress?: () => void
|
||||
}
|
||||
|
||||
export interface ImageUploaderNeoPayload {
|
||||
url: string
|
||||
mimeType: string | null
|
||||
// Offers section — identical in web-ui offers.tsx + user-ui order-again/index.tsx
|
||||
export interface OffersSectionProps {
|
||||
title: string
|
||||
subtitle: string
|
||||
products: any[]
|
||||
expanded: boolean
|
||||
onExpand: () => void
|
||||
onProductPress: (id: number) => void
|
||||
}
|
||||
|
||||
// Navigation redirect state — duplicated in user-ui hooks ↔ contexts
|
||||
export interface RedirectState {
|
||||
targetUrl: string
|
||||
queryParams: Record<string, any>
|
||||
timestamp: number
|
||||
// --- Cart stores (zustand state shapes, identical user-ui ↔ web-ui) ---
|
||||
export interface AddedToCartProduct {
|
||||
productId: number
|
||||
product: any
|
||||
}
|
||||
|
||||
// Vendor snippet product — duplicated admin-ui components ↔ types
|
||||
export interface VendorSnippetProduct {
|
||||
id: number
|
||||
name: string
|
||||
export interface CartStore {
|
||||
addedToCartProduct: AddedToCartProduct | null
|
||||
setAddedToCartProduct: (product: AddedToCartProduct | null) => void
|
||||
clearAddedToCartProduct: () => void
|
||||
}
|
||||
|
||||
// --- Store header (zustand state shape, identical user-ui ↔ web-ui) ---
|
||||
export interface StoreHeaderState {
|
||||
title: string
|
||||
setTitle: (title: string) => void
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,21 +21,3 @@ export interface UserNegativityData {
|
|||
userId: number
|
||||
totalNegativityScore: number
|
||||
}
|
||||
|
||||
export interface PlacedOrderShape {
|
||||
id: number
|
||||
userId: number
|
||||
addressId: number
|
||||
slotId: number | null
|
||||
totalAmount: string
|
||||
deliveryCharge: string
|
||||
isCod: boolean
|
||||
isOnlinePayment: boolean
|
||||
paymentInfoId: number | null
|
||||
readableId: number
|
||||
userNotes: string | null
|
||||
orderGroupId: string
|
||||
orderGroupProportion: string
|
||||
isFlashDelivery: boolean
|
||||
createdAt: Date
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ 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 './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
|
||||
|
|
|
|||
|
|
@ -1,15 +1,25 @@
|
|||
/**
|
||||
* Primitive shared shapes
|
||||
* Leaf module (no imports) — safe to import from anywhere without cycles.
|
||||
* Domain types that are exactly these shapes alias them instead of redeclaring.
|
||||
* Leaf module (no runtime imports — type-only imports are erased at compile,
|
||||
* so they cannot create cycles). Domain types that are exactly these shapes
|
||||
* alias them instead of redeclaring.
|
||||
*/
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
export interface MessageResponse {
|
||||
success: boolean
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface BasicSuccessResponse {
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export interface IdName {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface ReactParentComponent {
|
||||
children: ReactNode
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ export interface RolePermissionAssignment {
|
|||
permissionName: StaffPermissionName
|
||||
}
|
||||
|
||||
// Generic key-value seed for `keyValStore`
|
||||
export interface KeyValSeedData {
|
||||
key: string
|
||||
value: any
|
||||
}
|
||||
import type { Constant } from './const.types'
|
||||
|
||||
// Generic key-value seed for `keyValStore` — exactly the shared Constant shape
|
||||
export type KeyValSeedData = Constant
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// User-related types
|
||||
|
||||
import type { MessageResponse } from './primitives.types'
|
||||
import type { MessageResponse, BasicSuccessResponse } from './primitives.types'
|
||||
import type { AdminDeliverySlot } from './admin'
|
||||
|
||||
import type { Banner } from './banner.types'
|
||||
|
||||
|
|
@ -230,11 +231,9 @@ export interface UserStoreDetailData {
|
|||
products: UserStoreProductData[];
|
||||
}
|
||||
|
||||
export interface UserProductStoreInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string | null;
|
||||
}
|
||||
import type { StoreSummary } from './store.types'
|
||||
|
||||
export type UserProductStoreInfo = StoreSummary
|
||||
|
||||
export interface UserProductDeliverySlot {
|
||||
id: number;
|
||||
|
|
@ -324,16 +323,7 @@ export interface UserSlotAvailability {
|
|||
isOutOfStock: boolean;
|
||||
}
|
||||
|
||||
export interface UserDeliverySlot {
|
||||
id: number;
|
||||
deliveryTime: Date;
|
||||
freezeTime: Date;
|
||||
isActive: boolean;
|
||||
isFlash: boolean;
|
||||
isCapacityFull: boolean;
|
||||
deliverySequence: unknown;
|
||||
groupIds: unknown;
|
||||
}
|
||||
export type UserDeliverySlot = AdminDeliverySlot
|
||||
|
||||
export interface UserSlotsResponse {
|
||||
slots: UserSlotWithProducts[];
|
||||
|
|
@ -498,9 +488,7 @@ export interface UserProfileCompleteResponse {
|
|||
isComplete: boolean;
|
||||
}
|
||||
|
||||
export interface UserSavePushTokenResponse {
|
||||
success: boolean;
|
||||
}
|
||||
export type UserSavePushTokenResponse = BasicSuccessResponse
|
||||
|
||||
export interface UserOrderItemSummary {
|
||||
productName: string;
|
||||
|
|
|
|||
|
|
@ -3,12 +3,9 @@ import React from "react";
|
|||
import { KeyboardAvoidingView, Platform, ScrollView, View, RefreshControl } from "react-native";
|
||||
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
||||
import { useRefresh } from "../lib/refresh-context";
|
||||
import type { ReactParentComponent } from '@packages/shared';
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
function AppContainer(props: Props) {
|
||||
function AppContainer(props: ReactParentComponent) {
|
||||
const { children } = props;
|
||||
const { refreshAll } = useRefresh();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState } from 'react'
|
||||
import { cn } from '../lib/utils'
|
||||
|
||||
interface ImageCarouselProps {
|
||||
export interface ImageCarouselProps {
|
||||
images: { uri?: string }[]
|
||||
className?: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import React from 'react'
|
||||
import { cn } from '../lib/utils'
|
||||
import type { ImageCarouselProps } from './image-carousel'
|
||||
|
||||
interface ImageGalleryProps {
|
||||
images: { uri?: string }[]
|
||||
className?: string
|
||||
}
|
||||
type ImageGalleryProps = ImageCarouselProps
|
||||
|
||||
export function ImageGallery({ images, className }: ImageGalleryProps) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
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)
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
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')
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
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')
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
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')
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
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'}`)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
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(', '))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue