This commit is contained in:
2026-03-21 17:23:27 +03:30
parent 878d8fc544
commit 451a814347
17 changed files with 122 additions and 45 deletions
@@ -13,10 +13,10 @@ import UserList from '@views/apps/user/list'
// API Imports
import { userManagementService } from '@/libs/api'
import type { User } from '@/libs/api/services/userManagementService'
import type { UsersType } from '@/types/apps/userTypes'
const UserListApp = () => {
const [users, setUsers] = useState<User[]>([])
const [users, setUsers] = useState<UsersType[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
@@ -24,7 +24,7 @@ const UserListApp = () => {
const fetchUsers = async () => {
try {
setLoading(true)
const response = await userManagementService.getUsers()
const response = await userManagementService.getUsers() as any
setUsers(response.users)
} catch (err: any) {
setError(err.message || 'خطا در دریافت لیست کاربران')
+3 -2
View File
@@ -3,9 +3,10 @@ import Pagination from '@mui/material/Pagination'
import Typography from '@mui/material/Typography'
// Third Party Imports
import type { useReactTable } from '@tanstack/react-table'
import type { Table } from '@tanstack/react-table'
const TablePaginationComponent = ({ table }: { table: ReturnType<typeof useReactTable> }) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const TablePaginationComponent = ({ table }: { table: Table<any> }) => {
return (
<div className='flex justify-between items-center flex-wrap pli-6 border-bs bs-auto plb-[12.5px] gap-2'>
<Typography color='text.disabled'>
+2 -1
View File
@@ -1,4 +1,5 @@
import { getRequestConfig } from 'next-intl/server'
import type { AbstractIntlMessages } from 'next-intl'
import faMessages from '../../messages/fa.json'
export default getRequestConfig(async () => {
@@ -6,6 +7,6 @@ export default getRequestConfig(async () => {
return {
locale,
messages: faMessages as Record<string, unknown>
messages: faMessages as AbstractIntlMessages
}
})
+25 -4
View File
@@ -4,7 +4,16 @@
export * from './client'
export * from './types'
export * from './services/authService'
export {
type RequestOTPRequest,
type RequestOTPResponse,
type VerifyOTPRequest,
type AuthUser,
type VerifyOTPResponse,
type UpdateProfilePayload,
type UpdateProfileResponse,
authService
} from './services/authService'
export * from './services/taskService'
export * from './services/eventService'
export * from './services/simulatorService'
@@ -12,8 +21,20 @@ export * from './services/chatService'
export * from './services/aiChatService'
export * from './services/kanbanService'
export * from './services/todoService'
export * from './services/userManagementService'
export {
type User,
type UserDetails,
type Account,
type ApiResponse,
type UpdateProfileRequest,
type AddAccountRequest,
type UpdateAccountRequest,
userManagementService
} from './services/userManagementService'
export * from './services/rolesPermissionsService'
export * from './services/sensorHubService'
export * from './services/farmDashboardService'
export {
type FarmDashboardConfigResponse,
type FarmDashboardCardsResponse,
farmDashboardService
} from './services/farmDashboardService'
@@ -6,6 +6,36 @@
import { apiClient } from '../client'
export interface User {
id: number
username: string
email: string
first_name: string
last_name: string
phone_number: string
role?: string
status?: string
avatar?: string
fullName?: string
currentPlan?: string
billing?: string
company?: string
country?: string
contact?: string
}
export interface UserDetails {
id: number
username: string
email: string
first_name: string
last_name: string
phone_number: string
role?: string
status?: string
avatar?: string
}
export interface Account {
id: number
username: string
@@ -49,6 +79,31 @@ export interface UpdateAccountRequest {
}
export const userManagementService = {
/**
* Get list of users
*/
async getUsers(): Promise<{ users: User[]; stats: { total: number; active: number; pending: number; inactive: number } }> {
const response = await apiClient.get<ApiResponse<{ users: User[]; stats: { total: number; active: number; pending: number; inactive: number } }>>('/api/account/')
return response.data as { users: User[]; stats: { total: number; active: number; pending: number; inactive: number } }
},
/**
* Get a single user by ID
*/
async getUser(id: number): Promise<UserDetails> {
const response = await apiClient.get<ApiResponse<UserDetails>>(`/api/account/${id}/`)
return response.data as unknown as UserDetails
},
/**
* Delete a user by ID
*/
async deleteUser(id: number): Promise<void> {
await apiClient.delete<ApiResponse<unknown>>(`/api/account/${id}/`)
},
/**
* Update current user profile (first_name, last_name, email)
*/
+8 -8
View File
@@ -10,9 +10,9 @@ import type { BoxProps } from '@mui/material/Box'
// Third-party Imports
import { Calendar } from 'react-multi-date-picker'
import type { DateObject } from 'react-multi-date-picker'
import DateObject from 'react-date-object'
import persian from 'react-date-object/calendars/persian'
import fa from 'react-date-object/locales/fa'
import persian_fa from 'react-date-object/locales/persian_fa'
// Styles - base styles only, we override colors via sx
import 'react-multi-date-picker/styles/colors/teal.css'
@@ -30,23 +30,23 @@ const AppJalaliDatepicker = (props: AppJalaliDatepickerProps) => {
const [internalValue, setInternalValue] = useState<DateObject | undefined>(() => {
const d = externalValue ?? new Date()
return new DateObject(d, { calendar: persian, locale: fa })
return new DateObject({ date: d, calendar: persian, locale: persian_fa })
})
useEffect(() => {
if (externalValue != null) {
setInternalValue(new DateObject(externalValue, { calendar: persian, locale: fa }))
setInternalValue(new DateObject({ date: externalValue, calendar: persian, locale: persian_fa }))
}
}, [externalValue])
const handleChange = (d: DateObject | null) => {
if (d) {
setInternalValue(d)
setInternalValue(d as unknown as DateObject)
onChange?.(d.toDate())
}
}
const displayValue = internalValue ?? new DateObject({ calendar: persian, locale: fa })
const displayValue = internalValue ?? new DateObject({ calendar: persian, locale: persian_fa })
return (
<Box
@@ -87,9 +87,9 @@ const AppJalaliDatepicker = (props: AppJalaliDatepickerProps) => {
>
<Calendar
value={displayValue}
onChange={handleChange}
onChange={handleChange as any}
calendar={persian}
locale={fa}
locale={persian_fa}
className="teal"
/>
</Box>
+12 -13
View File
@@ -83,7 +83,7 @@ const Calendar = (props: CalenderProps) => {
},
views: {
week: {
titleFormat(arg) {
titleFormat(arg: any) {
const start = arg.start
const end = arg.end
@@ -94,20 +94,21 @@ const Calendar = (props: CalenderProps) => {
})
if (!start && !end) {
return { text: '' }
return ''
}
if (start && !end) {
return { text: formatter.format(start) }
return formatter.format(start instanceof Date ? start : new Date(start.marker ?? start))
}
if (!start && end) {
return { text: formatter.format(end) }
return formatter.format(end instanceof Date ? end : new Date(end.marker ?? end))
}
return {
text: `${formatter.format(start)} - ${formatter.format(end)}`
}
const s = start instanceof Date ? start : new Date(start.marker ?? start)
const e = end instanceof Date ? end : new Date(end.marker ?? end)
return `${formatter.format(s)} - ${formatter.format(e)}`
}
}
},
@@ -116,7 +117,7 @@ const Calendar = (props: CalenderProps) => {
return formatter.format(arg.date)
},
titleFormat(arg) {
titleFormat(arg: any) {
const { start, end } = arg
const monthFormatter = new Intl.DateTimeFormat('fa-IR-u-ca-persian', {
@@ -129,18 +130,16 @@ const Calendar = (props: CalenderProps) => {
const target = start || end
if (!target) {
return { text: '' }
return ''
}
const asDate = target instanceof Date ? target : new Date(target as any)
if (Number.isNaN(asDate.getTime())) {
return { text: '' }
return ''
}
return {
text: monthFormatter.format(asDate)
}
return monthFormatter.format(asDate)
},
buttonText: {
today: 'امروز',
@@ -44,7 +44,7 @@ const FarmDashboardSettingsDrawer = (props: FarmDashboardSettingsDrawerProps) =>
<Drawer
open={open}
onClose={onClose}
anchor='end'
anchor='right'
variant='temporary'
ModalProps={{
disablePortal: true,
@@ -54,7 +54,7 @@ const cardRowSx = {
'& > *': { flex: 1, minHeight: 0 }
}
const CARD_COMPONENTS: Record<CardId, React.ComponentType> = {
const CARD_COMPONENTS: Record<CardId, React.ComponentType<{ data?: Record<string, unknown> }>> = {
farmOverviewKpis: FarmOverviewKPIs,
farmWeatherCard: FarmWeatherCard,
farmAlertsTracker: FarmAlertsTracker,
@@ -37,7 +37,7 @@ const FarmOverviewKPIs = ({ data }: FarmOverviewKPIsProps) => {
avatarIcon={kpi.avatarIcon ?? 'tabler-chart-bar'}
avatarSkin='light'
avatarSize={44}
chipText={kpi.chipText}
chipText={kpi.chipText ?? ''}
chipColor={(kpi.chipColor as 'success' | 'warning') ?? 'success'}
chipVariant='tonal'
/>
@@ -26,10 +26,10 @@ interface FarmWeatherCardProps {
const FarmWeatherCard = ({ data }: FarmWeatherCardProps) => {
const t = useTranslations('farmDashboard')
const temperature = data?.temperature ?? 24
const temperature = (data?.temperature as number | undefined) ?? 24
const condition = (data?.condition as string) ?? ''
const humidity = data?.humidity ?? 45
const windSpeed = data?.windSpeed ?? 12
const humidity = (data?.humidity as number | undefined) ?? 45
const windSpeed = (data?.windSpeed as number | undefined) ?? 12
const windUnit = (data?.windUnit as string) ?? 'km/h'
const unit = (data?.unit as string) ?? '°C'
const chartData = data?.chartData as { labels?: string[]; series?: number[][] } | undefined
@@ -27,7 +27,7 @@ const SensorComparisonChart = ({ data }: SensorComparisonChartProps) => {
const categories =
(data?.categories as string[]) ??
[t('fallback.mon'), t('fallback.tue'), t('fallback.wed'), t('fallback.thu'), t('fallback.fri'), t('fallback.sat'), t('fallback.sun')]
const currentValue = data?.currentValue ?? 48
const currentValue = (data?.currentValue as number | undefined) ?? 48
const vsLastWeek = (data?.vsLastWeek as string) ?? t('fallback.plusPercentVsLastWeek', { val: '5' })
const theme = useTheme()
if (series.length === 0) return null
@@ -128,7 +128,7 @@ export default function CropZoningWeatherSection() {
</Typography>
<Typography variant='h5'>
{temp}
{weatherData.unit ?? '°C'}
{(weatherData.unit as string) ?? '°C'}
</Typography>
</CardContent>
</Card>
@@ -67,7 +67,7 @@ export default function ZoneDetailPanel({
<Drawer
open={open}
onClose={onClose}
anchor='end'
anchor='right'
variant='temporary'
ModalProps={{
disablePortal: true,
@@ -68,11 +68,11 @@ export const CARD_GRID_SIZE: Record<CardId, { xs?: number; sm?: number; md?: num
farmAlertsTimeline: { xs: 12, lg: 4 },
waterNeedPrediction: { xs: 12, lg: 8 },
harvestPredictionCard: { xs: 12, md: 6, lg: 4 },
yieldPredictionChart: { xs: 12 },
soilMoistureHeatmap: { xs: 12 },
yieldPredictionChart: { xs: 12, lg: 12 },
soilMoistureHeatmap: { xs: 12, lg: 12 },
ndviHealthCard: { xs: 12, md: 6, lg: 4 },
recommendationsList: { xs: 12, md: 6, lg: 8 },
economicOverview: { xs: 12 }
economicOverview: { xs: 12, lg: 12 }
}
/** Display label for each card (for settings UI) */
+1 -1
View File
@@ -29,7 +29,7 @@ type OptionSensorHubProps = {
const formatLastUpdated = (dateStr: string | null | undefined): string => {
if (!dateStr) return '—'
try {
const d = new DateObject(new Date(dateStr)).convert('persian').setLocale('fa')
const d = new DateObject(new Date(dateStr)).convert('persian' as any).setLocale('fa' as any)
return d.format('YYYY/MM/DD')
} catch {
+1 -1
View File
@@ -23,7 +23,7 @@ import styles from '@core/styles/table.module.css'
const formatToShamsi = (dateStr: string | null | undefined): string => {
if (!dateStr) return '—'
try {
const d = new DateObject(new Date(dateStr)).convert('persian').setLocale('fa')
const d = new DateObject(new Date(dateStr)).convert('persian' as any).setLocale('fa' as any)
return d.format('YYYY/MM/DD')
} catch {