CI/CD
This commit is contained in:
@@ -13,10 +13,10 @@ import UserList from '@views/apps/user/list'
|
|||||||
|
|
||||||
// API Imports
|
// API Imports
|
||||||
import { userManagementService } from '@/libs/api'
|
import { userManagementService } from '@/libs/api'
|
||||||
import type { User } from '@/libs/api/services/userManagementService'
|
import type { UsersType } from '@/types/apps/userTypes'
|
||||||
|
|
||||||
const UserListApp = () => {
|
const UserListApp = () => {
|
||||||
const [users, setUsers] = useState<User[]>([])
|
const [users, setUsers] = useState<UsersType[]>([])
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ const UserListApp = () => {
|
|||||||
const fetchUsers = async () => {
|
const fetchUsers = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const response = await userManagementService.getUsers()
|
const response = await userManagementService.getUsers() as any
|
||||||
setUsers(response.users)
|
setUsers(response.users)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err.message || 'خطا در دریافت لیست کاربران')
|
setError(err.message || 'خطا در دریافت لیست کاربران')
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ import Pagination from '@mui/material/Pagination'
|
|||||||
import Typography from '@mui/material/Typography'
|
import Typography from '@mui/material/Typography'
|
||||||
|
|
||||||
// Third Party Imports
|
// 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 (
|
return (
|
||||||
<div className='flex justify-between items-center flex-wrap pli-6 border-bs bs-auto plb-[12.5px] gap-2'>
|
<div className='flex justify-between items-center flex-wrap pli-6 border-bs bs-auto plb-[12.5px] gap-2'>
|
||||||
<Typography color='text.disabled'>
|
<Typography color='text.disabled'>
|
||||||
|
|||||||
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
import { getRequestConfig } from 'next-intl/server'
|
import { getRequestConfig } from 'next-intl/server'
|
||||||
|
import type { AbstractIntlMessages } from 'next-intl'
|
||||||
import faMessages from '../../messages/fa.json'
|
import faMessages from '../../messages/fa.json'
|
||||||
|
|
||||||
export default getRequestConfig(async () => {
|
export default getRequestConfig(async () => {
|
||||||
@@ -6,6 +7,6 @@ export default getRequestConfig(async () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
locale,
|
locale,
|
||||||
messages: faMessages as Record<string, unknown>
|
messages: faMessages as AbstractIntlMessages
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
+25
-4
@@ -4,7 +4,16 @@
|
|||||||
|
|
||||||
export * from './client'
|
export * from './client'
|
||||||
export * from './types'
|
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/taskService'
|
||||||
export * from './services/eventService'
|
export * from './services/eventService'
|
||||||
export * from './services/simulatorService'
|
export * from './services/simulatorService'
|
||||||
@@ -12,8 +21,20 @@ export * from './services/chatService'
|
|||||||
export * from './services/aiChatService'
|
export * from './services/aiChatService'
|
||||||
export * from './services/kanbanService'
|
export * from './services/kanbanService'
|
||||||
export * from './services/todoService'
|
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/rolesPermissionsService'
|
||||||
export * from './services/sensorHubService'
|
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'
|
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 {
|
export interface Account {
|
||||||
id: number
|
id: number
|
||||||
username: string
|
username: string
|
||||||
@@ -49,6 +79,31 @@ export interface UpdateAccountRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const userManagementService = {
|
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)
|
* Update current user profile (first_name, last_name, email)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import type { BoxProps } from '@mui/material/Box'
|
|||||||
|
|
||||||
// Third-party Imports
|
// Third-party Imports
|
||||||
import { Calendar } from 'react-multi-date-picker'
|
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 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
|
// Styles - base styles only, we override colors via sx
|
||||||
import 'react-multi-date-picker/styles/colors/teal.css'
|
import 'react-multi-date-picker/styles/colors/teal.css'
|
||||||
@@ -30,23 +30,23 @@ const AppJalaliDatepicker = (props: AppJalaliDatepickerProps) => {
|
|||||||
const [internalValue, setInternalValue] = useState<DateObject | undefined>(() => {
|
const [internalValue, setInternalValue] = useState<DateObject | undefined>(() => {
|
||||||
const d = externalValue ?? new Date()
|
const d = externalValue ?? new Date()
|
||||||
|
|
||||||
return new DateObject(d, { calendar: persian, locale: fa })
|
return new DateObject({ date: d, calendar: persian, locale: persian_fa })
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (externalValue != null) {
|
if (externalValue != null) {
|
||||||
setInternalValue(new DateObject(externalValue, { calendar: persian, locale: fa }))
|
setInternalValue(new DateObject({ date: externalValue, calendar: persian, locale: persian_fa }))
|
||||||
}
|
}
|
||||||
}, [externalValue])
|
}, [externalValue])
|
||||||
|
|
||||||
const handleChange = (d: DateObject | null) => {
|
const handleChange = (d: DateObject | null) => {
|
||||||
if (d) {
|
if (d) {
|
||||||
setInternalValue(d)
|
setInternalValue(d as unknown as DateObject)
|
||||||
onChange?.(d.toDate())
|
onChange?.(d.toDate())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayValue = internalValue ?? new DateObject({ calendar: persian, locale: fa })
|
const displayValue = internalValue ?? new DateObject({ calendar: persian, locale: persian_fa })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -87,9 +87,9 @@ const AppJalaliDatepicker = (props: AppJalaliDatepickerProps) => {
|
|||||||
>
|
>
|
||||||
<Calendar
|
<Calendar
|
||||||
value={displayValue}
|
value={displayValue}
|
||||||
onChange={handleChange}
|
onChange={handleChange as any}
|
||||||
calendar={persian}
|
calendar={persian}
|
||||||
locale={fa}
|
locale={persian_fa}
|
||||||
className="teal"
|
className="teal"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ const Calendar = (props: CalenderProps) => {
|
|||||||
},
|
},
|
||||||
views: {
|
views: {
|
||||||
week: {
|
week: {
|
||||||
titleFormat(arg) {
|
titleFormat(arg: any) {
|
||||||
const start = arg.start
|
const start = arg.start
|
||||||
const end = arg.end
|
const end = arg.end
|
||||||
|
|
||||||
@@ -94,20 +94,21 @@ const Calendar = (props: CalenderProps) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!start && !end) {
|
if (!start && !end) {
|
||||||
return { text: '' }
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start && !end) {
|
if (start && !end) {
|
||||||
return { text: formatter.format(start) }
|
return formatter.format(start instanceof Date ? start : new Date(start.marker ?? start))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!start && end) {
|
if (!start && end) {
|
||||||
return { text: formatter.format(end) }
|
return formatter.format(end instanceof Date ? end : new Date(end.marker ?? end))
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const s = start instanceof Date ? start : new Date(start.marker ?? start)
|
||||||
text: `${formatter.format(start)} - ${formatter.format(end)}`
|
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)
|
return formatter.format(arg.date)
|
||||||
},
|
},
|
||||||
titleFormat(arg) {
|
titleFormat(arg: any) {
|
||||||
const { start, end } = arg
|
const { start, end } = arg
|
||||||
|
|
||||||
const monthFormatter = new Intl.DateTimeFormat('fa-IR-u-ca-persian', {
|
const monthFormatter = new Intl.DateTimeFormat('fa-IR-u-ca-persian', {
|
||||||
@@ -129,18 +130,16 @@ const Calendar = (props: CalenderProps) => {
|
|||||||
const target = start || end
|
const target = start || end
|
||||||
|
|
||||||
if (!target) {
|
if (!target) {
|
||||||
return { text: '' }
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const asDate = target instanceof Date ? target : new Date(target as any)
|
const asDate = target instanceof Date ? target : new Date(target as any)
|
||||||
|
|
||||||
if (Number.isNaN(asDate.getTime())) {
|
if (Number.isNaN(asDate.getTime())) {
|
||||||
return { text: '' }
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return monthFormatter.format(asDate)
|
||||||
text: monthFormatter.format(asDate)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
buttonText: {
|
buttonText: {
|
||||||
today: 'امروز',
|
today: 'امروز',
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const FarmDashboardSettingsDrawer = (props: FarmDashboardSettingsDrawerProps) =>
|
|||||||
<Drawer
|
<Drawer
|
||||||
open={open}
|
open={open}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
anchor='end'
|
anchor='right'
|
||||||
variant='temporary'
|
variant='temporary'
|
||||||
ModalProps={{
|
ModalProps={{
|
||||||
disablePortal: true,
|
disablePortal: true,
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const cardRowSx = {
|
|||||||
'& > *': { flex: 1, minHeight: 0 }
|
'& > *': { flex: 1, minHeight: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
const CARD_COMPONENTS: Record<CardId, React.ComponentType> = {
|
const CARD_COMPONENTS: Record<CardId, React.ComponentType<{ data?: Record<string, unknown> }>> = {
|
||||||
farmOverviewKpis: FarmOverviewKPIs,
|
farmOverviewKpis: FarmOverviewKPIs,
|
||||||
farmWeatherCard: FarmWeatherCard,
|
farmWeatherCard: FarmWeatherCard,
|
||||||
farmAlertsTracker: FarmAlertsTracker,
|
farmAlertsTracker: FarmAlertsTracker,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const FarmOverviewKPIs = ({ data }: FarmOverviewKPIsProps) => {
|
|||||||
avatarIcon={kpi.avatarIcon ?? 'tabler-chart-bar'}
|
avatarIcon={kpi.avatarIcon ?? 'tabler-chart-bar'}
|
||||||
avatarSkin='light'
|
avatarSkin='light'
|
||||||
avatarSize={44}
|
avatarSize={44}
|
||||||
chipText={kpi.chipText}
|
chipText={kpi.chipText ?? ''}
|
||||||
chipColor={(kpi.chipColor as 'success' | 'warning') ?? 'success'}
|
chipColor={(kpi.chipColor as 'success' | 'warning') ?? 'success'}
|
||||||
chipVariant='tonal'
|
chipVariant='tonal'
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ interface FarmWeatherCardProps {
|
|||||||
|
|
||||||
const FarmWeatherCard = ({ data }: FarmWeatherCardProps) => {
|
const FarmWeatherCard = ({ data }: FarmWeatherCardProps) => {
|
||||||
const t = useTranslations('farmDashboard')
|
const t = useTranslations('farmDashboard')
|
||||||
const temperature = data?.temperature ?? 24
|
const temperature = (data?.temperature as number | undefined) ?? 24
|
||||||
const condition = (data?.condition as string) ?? ''
|
const condition = (data?.condition as string) ?? ''
|
||||||
const humidity = data?.humidity ?? 45
|
const humidity = (data?.humidity as number | undefined) ?? 45
|
||||||
const windSpeed = data?.windSpeed ?? 12
|
const windSpeed = (data?.windSpeed as number | undefined) ?? 12
|
||||||
const windUnit = (data?.windUnit as string) ?? 'km/h'
|
const windUnit = (data?.windUnit as string) ?? 'km/h'
|
||||||
const unit = (data?.unit as string) ?? '°C'
|
const unit = (data?.unit as string) ?? '°C'
|
||||||
const chartData = data?.chartData as { labels?: string[]; series?: number[][] } | undefined
|
const chartData = data?.chartData as { labels?: string[]; series?: number[][] } | undefined
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const SensorComparisonChart = ({ data }: SensorComparisonChartProps) => {
|
|||||||
const categories =
|
const categories =
|
||||||
(data?.categories as string[]) ??
|
(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')]
|
[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 vsLastWeek = (data?.vsLastWeek as string) ?? t('fallback.plusPercentVsLastWeek', { val: '5' })
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
if (series.length === 0) return null
|
if (series.length === 0) return null
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export default function CropZoningWeatherSection() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant='h5'>
|
<Typography variant='h5'>
|
||||||
{temp}
|
{temp}
|
||||||
{weatherData.unit ?? '°C'}
|
{(weatherData.unit as string) ?? '°C'}
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export default function ZoneDetailPanel({
|
|||||||
<Drawer
|
<Drawer
|
||||||
open={open}
|
open={open}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
anchor='end'
|
anchor='right'
|
||||||
variant='temporary'
|
variant='temporary'
|
||||||
ModalProps={{
|
ModalProps={{
|
||||||
disablePortal: true,
|
disablePortal: true,
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ export const CARD_GRID_SIZE: Record<CardId, { xs?: number; sm?: number; md?: num
|
|||||||
farmAlertsTimeline: { xs: 12, lg: 4 },
|
farmAlertsTimeline: { xs: 12, lg: 4 },
|
||||||
waterNeedPrediction: { xs: 12, lg: 8 },
|
waterNeedPrediction: { xs: 12, lg: 8 },
|
||||||
harvestPredictionCard: { xs: 12, md: 6, lg: 4 },
|
harvestPredictionCard: { xs: 12, md: 6, lg: 4 },
|
||||||
yieldPredictionChart: { xs: 12 },
|
yieldPredictionChart: { xs: 12, lg: 12 },
|
||||||
soilMoistureHeatmap: { xs: 12 },
|
soilMoistureHeatmap: { xs: 12, lg: 12 },
|
||||||
ndviHealthCard: { xs: 12, md: 6, lg: 4 },
|
ndviHealthCard: { xs: 12, md: 6, lg: 4 },
|
||||||
recommendationsList: { xs: 12, md: 6, lg: 8 },
|
recommendationsList: { xs: 12, md: 6, lg: 8 },
|
||||||
economicOverview: { xs: 12 }
|
economicOverview: { xs: 12, lg: 12 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Display label for each card (for settings UI) */
|
/** Display label for each card (for settings UI) */
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ type OptionSensorHubProps = {
|
|||||||
const formatLastUpdated = (dateStr: string | null | undefined): string => {
|
const formatLastUpdated = (dateStr: string | null | undefined): string => {
|
||||||
if (!dateStr) return '—'
|
if (!dateStr) return '—'
|
||||||
try {
|
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')
|
return d.format('YYYY/MM/DD')
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import styles from '@core/styles/table.module.css'
|
|||||||
const formatToShamsi = (dateStr: string | null | undefined): string => {
|
const formatToShamsi = (dateStr: string | null | undefined): string => {
|
||||||
if (!dateStr) return '—'
|
if (!dateStr) return '—'
|
||||||
try {
|
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')
|
return d.format('YYYY/MM/DD')
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
Reference in New Issue
Block a user