This commit is contained in:
shafi54 2026-03-04 01:03:13 +05:30
parent ed7318f9ee
commit dc644aef7e
5 changed files with 226 additions and 11 deletions

View file

@ -1,6 +1,6 @@
ENV_MODE=PROD ENV_MODE=PROD
DATABASE_URL=postgresql://postgres:meatfarmer_master_password@57.128.212.174:7447/meatfarmer #technocracy # DATABASE_URL=postgresql://postgres:meatfarmer_master_password@57.128.212.174:7447/meatfarmer #technocracy
# DATABASE_URL=postgres://postgres:meatfarmer_master_password@5.223.55.14:7447/meatfarmer #hetzner DATABASE_URL=postgres://postgres:meatfarmer_master_password@5.223.55.14:7447/meatfarmer #hetzner
PHONE_PE_BASE_URL=https://api-preprod.phonepe.com/ PHONE_PE_BASE_URL=https://api-preprod.phonepe.com/
PHONE_PE_CLIENT_ID=TEST-M23F2IGP34ZAR_25090 PHONE_PE_CLIENT_ID=TEST-M23F2IGP34ZAR_25090
PHONE_PE_CLIENT_VERSION=1 PHONE_PE_CLIENT_VERSION=1

View file

@ -9,6 +9,7 @@ import { UserHomeRoute } from './routes/user-home'
import { SuperAdminRoute } from './routes/super-admin' import { SuperAdminRoute } from './routes/super-admin'
import { CreateCouponRoute } from './routes/create-coupon' import { CreateCouponRoute } from './routes/create-coupon'
import { LocationMarkerRoute } from './routes/location-marker' import { LocationMarkerRoute } from './routes/location-marker'
import { UserConnectRoute } from './routes/user-connect'
import Inauguration from './routes/inauguration' import Inauguration from './routes/inauguration'
import { AuthWrapper } from './components/AuthWrapper' import { AuthWrapper } from './components/AuthWrapper'
import { SuperAdminGuard } from './components/SuperAdminGuard' import { SuperAdminGuard } from './components/SuperAdminGuard'
@ -101,6 +102,18 @@ const createCouponRoute = new Route({
) )
}) })
const userConnectRoute = new Route({
getParentRoute: () => rootRoute,
path: '/user-connect',
component: () => (
<Suspense fallback={<p>Loading user connect</p>}>
<AuthWrapper>
<UserConnectRoute />
</AuthWrapper>
</Suspense>
)
})
const locationMarkerRoute = new Route({ const locationMarkerRoute = new Route({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
path: '/location-marker', path: '/location-marker',
@ -118,6 +131,7 @@ const routeTree = rootRoute.addChildren([
userHomeRoute, userHomeRoute,
superAdminRoute, superAdminRoute,
createCouponRoute, createCouponRoute,
userConnectRoute,
locationMarkerRoute, locationMarkerRoute,
inaugurationRoute inaugurationRoute
]) ])
@ -139,7 +153,8 @@ declare module '@tanstack/react-router' {
const navItems = [ const navItems = [
{ to: '/', label: 'Dashboard', exact: true }, { to: '/', label: 'Dashboard', exact: true },
{ to: '/vendor-order-list', label: 'Vendor Order List' } { to: '/vendor-order-list', label: 'Vendor Order List' },
{ to: '/user-connect', label: 'User Connect' }
] as const ] as const
function RootComponent() { function RootComponent() {

View file

@ -1,11 +1,33 @@
import { useNavigate } from '@tanstack/react-router'
import { useUserStore } from '@/stores/userStore'
import { removeAuthToken } from '@/services/auth'
import { StaffUserForm } from '@/components/StaffUserForm'; import { StaffUserForm } from '@/components/StaffUserForm';
export function SuperAdminRoute() { export function SuperAdminRoute() {
const navigate = useNavigate()
const clearUser = useUserStore((state) => state.clearUser)
const handleLogout = async () => {
await removeAuthToken()
clearUser()
navigate({ to: '/login' as any })
}
return ( return (
<div className="max-w-6xl mx-auto space-y-6"> <div className="max-w-6xl mx-auto space-y-6">
<div className="bg-white rounded-lg shadow-md p-6"> <div className="bg-white rounded-lg shadow-md p-6">
<h1 className="text-3xl font-bold text-gray-900 mb-2">Super Admin Dashboard</h1> <div className="flex justify-between items-start">
<p className="text-gray-600">Advanced system management and user administration</p> <div>
<h1 className="text-3xl font-bold text-gray-900 mb-2">Super Admin Dashboard</h1>
<p className="text-gray-600">Advanced system management and user administration</p>
</div>
<button
onClick={handleLogout}
className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
>
Logout
</button>
</div>
</div> </div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">

View file

@ -0,0 +1,175 @@
import { useState } from 'react'
import { trpc } from '@/trpc/client'
export function UserConnectRoute() {
const [search, setSearch] = useState('')
const [searchInput, setSearchInput] = useState('')
const [cursor, setCursor] = useState<number | undefined>(undefined)
const { data, isLoading, error } = trpc.admin.user.getAllUsers.useQuery({
limit: 50,
cursor,
search,
})
const handleSearch = () => {
setSearch(searchInput)
setCursor(undefined)
}
const handleNextPage = () => {
if (data?.nextCursor) {
setCursor(data.nextCursor)
}
}
const handlePrevPage = () => {
setCursor(undefined)
}
if (isLoading) {
return (
<div className="max-w-6xl mx-auto">
<div className="bg-white rounded-lg shadow-md p-6">
<p className="text-gray-600 text-center">Loading users...</p>
</div>
</div>
)
}
if (error) {
return (
<div className="max-w-6xl mx-auto">
<div className="bg-white rounded-lg shadow-md p-6">
<p className="text-red-600 text-center">Error loading users: {error.message}</p>
</div>
</div>
)
}
return (
<div className="max-w-6xl mx-auto space-y-6">
<div className="bg-white rounded-lg shadow-md p-6">
<h1 className="text-3xl font-bold text-gray-900 mb-2">User Connect</h1>
<p className="text-gray-600">View all registered users and their contact information</p>
</div>
<div className="bg-white rounded-lg shadow-md p-6">
<div className="flex gap-2 mb-4">
<input
type="text"
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
placeholder="Search by mobile number..."
className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleSearch()
}
}}
/>
<button
onClick={handleSearch}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Search
</button>
{search && (
<button
onClick={() => {
setSearch('')
setSearchInput('')
setCursor(undefined)
}}
className="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"
>
Clear
</button>
)}
</div>
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-gray-200">
<th className="text-left py-3 px-4 font-semibold text-gray-700">ID</th>
<th className="text-left py-3 px-4 font-semibold text-gray-700">Name</th>
<th className="text-left py-3 px-4 font-semibold text-gray-700">Mobile</th>
<th className="text-left py-3 px-4 font-semibold text-gray-700">Total Orders</th>
<th className="text-left py-3 px-4 font-semibold text-gray-700">Last Order</th>
<th className="text-left py-3 px-4 font-semibold text-gray-700">Status</th>
</tr>
</thead>
<tbody>
{data?.users.map((user) => (
<tr key={user.id} className="border-b border-gray-100 hover:bg-gray-50">
<td className="py-3 px-4 text-gray-600">{user.id}</td>
<td className="py-3 px-4 text-gray-900">{user.name || '-'}</td>
<td className="py-3 px-4">
{user.mobile ? (
<a
href={`https://wa.me/91${user.mobile.replace(/\D/g, '')}`}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:text-blue-800 font-mono hover:underline"
>
{user.mobile}
</a>
) : (
<span className="text-gray-400">-</span>
)}
</td>
<td className="py-3 px-4 text-gray-600">{user.totalOrders}</td>
<td className="py-3 px-4 text-gray-600">
{user.lastOrderDate
? new Date(user.lastOrderDate).toLocaleDateString()
: '-'}
</td>
<td className="py-3 px-4">
{user.isSuspended ? (
<span className="px-2 py-1 bg-red-100 text-red-700 text-xs font-medium rounded">
Suspended
</span>
) : (
<span className="px-2 py-1 bg-green-100 text-green-700 text-xs font-medium rounded">
Active
</span>
)}
</td>
</tr>
))}
</tbody>
</table>
{data?.users.length === 0 && (
<p className="text-center text-gray-500 py-8">No users found</p>
)}
</div>
<div className="flex justify-between items-center mt-4 pt-4 border-t border-gray-200">
<p className="text-sm text-gray-600">
Showing {data?.users.length || 0} users
</p>
<div className="flex gap-2">
{cursor !== undefined && (
<button
onClick={handlePrevPage}
className="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"
>
Previous
</button>
)}
{data?.hasMore && (
<button
onClick={handleNextPage}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Next
</button>
)}
</div>
</div>
</div>
</div>
)
}

View file

@ -26,18 +26,21 @@ function AdminDashboard() {
</button> </button>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="bg-blue-50 p-4 rounded-lg"> <Link
to="/user-connect"
className="bg-blue-50 p-4 rounded-lg hover:bg-blue-100 transition-colors"
>
<h3 className="font-semibold text-blue-900 mb-2">User Management</h3> <h3 className="font-semibold text-blue-900 mb-2">User Management</h3>
<p className="text-blue-700 text-sm">Manage staff users and permissions</p> <p className="text-blue-700 text-sm">View all users and their contact info</p>
</div> </Link>
<div className="bg-green-50 p-4 rounded-lg"> {/*<div className="bg-green-50 p-4 rounded-lg">
<h3 className="font-semibold text-green-900 mb-2">System Settings</h3> <h3 className="font-semibold text-green-900 mb-2">System Settings</h3>
<p className="text-green-700 text-sm">Configure system-wide settings</p> <p className="text-green-700 text-sm">Configure system-wide settings</p>
</div> </div>
<div className="bg-purple-50 p-4 rounded-lg"> <div className="bg-purple-50 p-4 rounded-lg">
<h3 className="font-semibold text-purple-900 mb-2">Analytics</h3> <h3 className="font-semibold text-purple-900 mb-2">Analytics</h3>
<p className="text-purple-700 text-sm">View system analytics and reports</p> <p className="text-purple-700 text-sm">View system analytics and reports</p>
</div> </div>*/}
{user?.role?.name === 'super_admin' && ( {user?.role?.name === 'super_admin' && (
<Link <Link
to="/super-admin" to="/super-admin"