sozsoft-platform/configs/seeds/data/Sas.DeveloperKit.CustomComponents/1bca19aa-e144-5e4c-b23b-9eddd4dafd19.json
2026-10-19 17:35:08 +03:00

29 lines
8.1 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"ListFormCode": "Sas.DeveloperKit.CustomComponents",
"KeyFieldName": "Id",
"GeneratedAt": "2026-10-05T06:00:00Z",
"Order": 0,
"Rows": [
{
"Id": "1bca19aa-e144-5e4c-b23b-9eddd4dafd19",
"Name": "MyTimesheetWidget",
"RoutePath": null,
"Description": "Intranet panosu: çalışanın aylık puantajı (seçilen ay) ve yıllık toplamı, fazla mesai sınırı; Puantajım bağlantısı",
"IsActive": true,
"Dependencies": [],
"Code": "// Calisanin kendi puantaj ozeti. Veri Puantajim ekraninin sorgusundan okunur: satir suzmesi\n// (EmployeeId = oturum acan kullanici) ve yetki ListForm'da kalir, bilesen yalnizca toplar.\nconst MONTHLY = 'Hr.MyTimesheet'\n\nconst loadMonths = async () => {\n const response = await apiService.fetchData({\n method: 'GET',\n url: '/api/app/list-form-select/select',\n params: { listFormCode: MONTHLY, take: 500 },\n })\n return response.data?.data ?? []\n}\n\nconst num = (value) => Number(value ?? 0)\nconst format = (value) => {\n const number = num(value)\n return Number.isInteger(number) ? String(number) : number.toFixed(1)\n}\nconst sum = (rows, field) => rows.reduce((total, row) => total + num(row[field]), 0)\nconst periodOf = (row) => num(row.PeriodYear) * 100 + num(row.PeriodMonth)\nconst periodLabel = (period) => `${String(period % 100).padStart(2, '0')}.${Math.floor(period / 100)}`\n\nconst Stat = ({ label, value, className }) => (\n <div className=\"rounded-lg border border-gray-200 bg-gray-50 p-2 text-center dark:border-gray-700 dark:bg-gray-900/40\">\n <div className={'text-lg font-semibold ' + className}>{value}</div>\n <div className=\"truncate text-xs text-gray-500 dark:text-gray-400\">{label}</div>\n </div>\n)\n\nconst MyTimesheetWidget = () => {\n const [rows, setRows] = useState([])\n const [period, setPeriod] = useState(0)\n const [loading, setLoading] = useState(true)\n\n useEffect(() => {\n let active = true\n loadMonths()\n .then((data) => {\n if (!active) return\n const now = new Date()\n const current = now.getFullYear() * 100 + now.getMonth() + 1\n const periods = data.map(periodOf)\n setRows(data)\n setPeriod(periods.includes(current) ? current : Math.max(current, ...periods))\n })\n .catch(() => {})\n .finally(() => active && setLoading(false))\n return () => {\n active = false\n }\n }, [])\n\n const t = (key, params) => translate(`::Sas.DashboardWidget.Timesheet.${key}`, params)\n const periods = [...new Set(rows.map(periodOf))].sort((a, b) => b - a)\n const month = rows.find((row) => periodOf(row) === period) ?? {}\n const yearRows = rows.filter((row) => num(row.PeriodYear) === Math.floor(period / 100))\n const yearOvertime = sum(yearRows, 'OvertimeHours')\n const limit = num(yearRows[0]?.AnnualOvertimeLimit) || 270\n const ratio = Math.min(100, Math.round((yearOvertime / limit) * 100))\n const barColor = ratio >= 100 ? 'bg-red-500' : ratio >= 80 ? 'bg-orange-500' : 'bg-indigo-500'\n\n return (\n <div className=\"overflow-hidden rounded-xl border border-gray-200/50 bg-white shadow-lg dark:border-gray-700/50 dark:bg-gray-800\">\n <div className=\"flex items-center justify-between gap-2 border-b border-gray-200 p-4 dark:border-gray-700\">\n <div className=\"flex items-center gap-2\">\n <PlatformIcon name=\"FaBusinessTime\" className=\"h-5 w-5 text-indigo-500\" />\n <h2 className=\"text-base font-semibold text-gray-900 dark:text-white\">\n {translate('::Sas.DashboardWidget.MyTimesheet')}\n </h2>\n </div>\n {periods.length > 0 && (\n <select\n value={period}\n onChange={(event) => setPeriod(Number(event.target.value))}\n className=\"rounded-md border border-gray-300 bg-white px-2 py-1 text-xs text-gray-700 dark:border-gray-600 dark:bg-gray-900 dark:text-gray-200\"\n >\n {periods.map((value) => (\n <option key={value} value={value}>\n {periodLabel(value)}\n </option>\n ))}\n </select>\n )}\n </div>\n <div className=\"space-y-3 p-3\">\n {loading ? (\n <div className=\"p-4 text-center text-sm text-gray-500\">...</div>\n ) : rows.length === 0 ? (\n <div className=\"text-sm text-gray-500 dark:text-gray-400\">{t('NoData')}</div>\n ) : (\n <>\n <div className=\"grid grid-cols-3 gap-2\">\n <Stat label={t('NetHours')} value={format(month.NetHours)} className=\"text-indigo-600 dark:text-indigo-400\" />\n <Stat label={t('WorkDays')} value={format(month.WorkDays)} className=\"text-gray-900 dark:text-white\" />\n <Stat label={t('Overtime')} value={format(month.OvertimeHours)} className=\"text-purple-600 dark:text-purple-400\" />\n <Stat\n label={t('Late')}\n value={format(month.LateMinutes)}\n className={num(month.LateMinutes) > 0 ? 'text-orange-500' : 'text-gray-900 dark:text-white'}\n />\n <Stat\n label={t('EarlyLeave')}\n value={format(month.EarlyLeaveMinutes)}\n className={num(month.EarlyLeaveMinutes) > 0 ? 'text-orange-500' : 'text-gray-900 dark:text-white'}\n />\n <Stat\n label={t('Absent')}\n value={format(month.AbsentDays)}\n className={num(month.AbsentDays) > 0 ? 'text-red-600' : 'text-gray-900 dark:text-white'}\n />\n </div>\n <div className=\"flex flex-wrap gap-x-3 gap-y-1 text-xs text-gray-600 dark:text-gray-300\">\n <span>{t('LeaveDays', { days: format(month.LeaveDays) })}</span>\n <span>{t('SickDays', { days: format(month.SickDays) })}</span>\n <span>{t('HolidayDays', { days: format(num(month.HolidayDays) + num(month.WeeklyOffDays)) })}</span>\n </div>\n <div className=\"rounded-lg border border-gray-200 p-2 dark:border-gray-700\">\n <div className=\"mb-1 text-xs font-semibold text-gray-500 dark:text-gray-400\">\n {t('Year', { year: Math.floor(period / 100) })}\n </div>\n <div className=\"grid grid-cols-3 gap-2 text-center text-xs text-gray-600 dark:text-gray-300\">\n <div>\n <div className=\"text-sm font-semibold text-gray-900 dark:text-white\">{format(sum(yearRows, 'NetHours'))}</div>\n {t('NetHours')}\n </div>\n <div>\n <div className=\"text-sm font-semibold text-gray-900 dark:text-white\">{format(sum(yearRows, 'LateMinutes'))}</div>\n {t('Late')}\n </div>\n <div>\n <div className=\"text-sm font-semibold text-gray-900 dark:text-white\">{format(sum(yearRows, 'AbsentDays'))}</div>\n {t('Absent')}\n </div>\n </div>\n <div className=\"mt-2 text-xs text-gray-600 dark:text-gray-300\">\n {t('OvertimeLimit', { used: format(yearOvertime), limit: format(limit) })}\n </div>\n <div className=\"mt-1 h-2 overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700\">\n <div className={'h-full rounded-full ' + barColor} style={{ width: ratio + '%' }} />\n </div>\n </div>\n </>\n )}\n <div className=\"flex flex-wrap gap-2 pt-1\">\n <a\n href={'/admin/list/' + MONTHLY}\n className=\"rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-indigo-700\"\n >\n {t('Open')}\n </a>\n </div>\n </div>\n </div>\n )\n}\n\nexport default MyTimesheetWidget\n",
"Props": null,
"DataSources": [],
"SeoTitle": null,
"SeoDescription": null,
"SeoKeywords": null,
"SeoOgImage": null,
"SeoCanonicalUrl": null,
"SeoNoIndex": true,
"SeoIncludeInSitemap": false,
"SeoChangeFrequency": null,
"SeoPriority": null,
"SeoStructuredData": null
}
]
}