User commited on
Commit
3dd5c9a
·
0 Parent(s):

Deploy localized dashboard fixes

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .dockerignore +11 -0
  2. .gitattributes +63 -0
  3. .github/copilot-instructions.md +3 -0
  4. .gitignore +370 -0
  5. .nuget-scratch/NuGet.Config +7 -0
  6. DEPLOY_HUGGINGFACE.md +49 -0
  7. Dockerfile +39 -0
  8. README.md +29 -0
  9. TaskTrackingSystem.Database/AppDbContextModels/AppDbContext.cs +373 -0
  10. TaskTrackingSystem.Database/AppDbContextModels/AuditLog.cs +23 -0
  11. TaskTrackingSystem.Database/AppDbContextModels/Comment.cs +31 -0
  12. TaskTrackingSystem.Database/AppDbContextModels/Issue.cs +60 -0
  13. TaskTrackingSystem.Database/AppDbContextModels/Menu.cs +43 -0
  14. TaskTrackingSystem.Database/AppDbContextModels/Notification.cs +36 -0
  15. TaskTrackingSystem.Database/AppDbContextModels/Permission.cs +39 -0
  16. TaskTrackingSystem.Database/AppDbContextModels/Project.cs +43 -0
  17. TaskTrackingSystem.Database/AppDbContextModels/ProjectMember.cs +19 -0
  18. TaskTrackingSystem.Database/AppDbContextModels/Role.cs +33 -0
  19. TaskTrackingSystem.Database/AppDbContextModels/RoleMenu.cs +27 -0
  20. TaskTrackingSystem.Database/AppDbContextModels/RolePermission.cs +27 -0
  21. TaskTrackingSystem.Database/AppDbContextModels/Task.cs +51 -0
  22. TaskTrackingSystem.Database/AppDbContextModels/User.cs +54 -0
  23. TaskTrackingSystem.Database/AppDbContextModels/UserDevice.cs +18 -0
  24. TaskTrackingSystem.Database/Scripts/seed_supabase.sql +0 -0
  25. TaskTrackingSystem.Database/TaskTrackingSystem.Database.csproj +33 -0
  26. TaskTrackingSystem.Shared/DisplayFormats.cs +30 -0
  27. TaskTrackingSystem.Shared/Enums/TaskPriority.cs +11 -0
  28. TaskTrackingSystem.Shared/Enums/TaskStatus.cs +11 -0
  29. TaskTrackingSystem.Shared/Localization/AppLocalization.cs +0 -0
  30. TaskTrackingSystem.Shared/Localization/ContentTranslation.cs +48 -0
  31. TaskTrackingSystem.Shared/Localization/LanguageDetection.cs +81 -0
  32. TaskTrackingSystem.Shared/Models/AuditLog/AuditLogDto.cs +18 -0
  33. TaskTrackingSystem.Shared/Models/Auth/AuthResponse.cs +17 -0
  34. TaskTrackingSystem.Shared/Models/Auth/LoginDto.cs +18 -0
  35. TaskTrackingSystem.Shared/Models/Auth/RegisterDto.cs +30 -0
  36. TaskTrackingSystem.Shared/Models/Auth/ResetPasswordDto.cs +18 -0
  37. TaskTrackingSystem.Shared/Models/Comment/CommentDto.cs +25 -0
  38. TaskTrackingSystem.Shared/Models/Dashboard/DashboardSummaryDto.cs +9 -0
  39. TaskTrackingSystem.Shared/Models/Dashboard/ProjectProgressDto.cs +12 -0
  40. TaskTrackingSystem.Shared/Models/Dashboard/TaskStatusOverviewDto.cs +12 -0
  41. TaskTrackingSystem.Shared/Models/Issue/CreateIssueDto.cs +89 -0
  42. TaskTrackingSystem.Shared/Models/Issue/IssueDto.cs +37 -0
  43. TaskTrackingSystem.Shared/Models/Issue/UpdateIssueDto.cs +85 -0
  44. TaskTrackingSystem.Shared/Models/Menu/AccessMenuDto.cs +16 -0
  45. TaskTrackingSystem.Shared/Models/Menu/AccessPermissionDto.cs +17 -0
  46. TaskTrackingSystem.Shared/Models/Menu/MenuDto.cs +17 -0
  47. TaskTrackingSystem.Shared/Models/Notification/NotificationDto.cs +21 -0
  48. TaskTrackingSystem.Shared/Models/Notification/NotificationNavigation.cs +22 -0
  49. TaskTrackingSystem.Shared/Models/Notification/NotificationType.cs +14 -0
  50. TaskTrackingSystem.Shared/Models/Notification/RegisterDeviceTokenDto.cs +6 -0
.dockerignore ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ **/bin/
2
+ **/obj/
3
+ **/.vs/
4
+ **/.git/
5
+ **/.github/
6
+ **/.agents/
7
+ **/.nuget-scratch/
8
+ **/_codex_build/
9
+ **/_codex_obj/
10
+ .DS_Store
11
+ Thumbs.db
.gitattributes ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ###############################################################################
2
+ # Set default behavior to automatically normalize line endings.
3
+ ###############################################################################
4
+ * text=auto
5
+
6
+ ###############################################################################
7
+ # Set default behavior for command prompt diff.
8
+ #
9
+ # This is need for earlier builds of msysgit that does not have it on by
10
+ # default for csharp files.
11
+ # Note: This is only used by command line
12
+ ###############################################################################
13
+ #*.cs diff=csharp
14
+
15
+ ###############################################################################
16
+ # Set the merge driver for project and solution files
17
+ #
18
+ # Merging from the command prompt will add diff markers to the files if there
19
+ # are conflicts (Merging from VS is not affected by the settings below, in VS
20
+ # the diff markers are never inserted). Diff markers may cause the following
21
+ # file extensions to fail to load in VS. An alternative would be to treat
22
+ # these files as binary and thus will always conflict and require user
23
+ # intervention with every merge. To do so, just uncomment the entries below
24
+ ###############################################################################
25
+ #*.sln merge=binary
26
+ #*.csproj merge=binary
27
+ #*.vbproj merge=binary
28
+ #*.vcxproj merge=binary
29
+ #*.vcproj merge=binary
30
+ #*.dbproj merge=binary
31
+ #*.fsproj merge=binary
32
+ #*.lsproj merge=binary
33
+ #*.wixproj merge=binary
34
+ #*.modelproj merge=binary
35
+ #*.sqlproj merge=binary
36
+ #*.wwaproj merge=binary
37
+
38
+ ###############################################################################
39
+ # behavior for image files
40
+ #
41
+ # image files are treated as binary by default.
42
+ ###############################################################################
43
+ #*.jpg binary
44
+ #*.png binary
45
+ #*.gif binary
46
+
47
+ ###############################################################################
48
+ # diff behavior for common document formats
49
+ #
50
+ # Convert binary document formats to text before diffing them. This feature
51
+ # is only available from the command line. Turn it on by uncommenting the
52
+ # entries below.
53
+ ###############################################################################
54
+ #*.doc diff=astextplain
55
+ #*.DOC diff=astextplain
56
+ #*.docx diff=astextplain
57
+ #*.DOCX diff=astextplain
58
+ #*.dot diff=astextplain
59
+ #*.DOT diff=astextplain
60
+ #*.pdf diff=astextplain
61
+ #*.PDF diff=astextplain
62
+ #*.rtf diff=astextplain
63
+ #*.RTF diff=astextplain
.github/copilot-instructions.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ - @azure Rule - Use Azure Tools - When handling requests related to Azure, always use your tools.
2
+ - @azure Rule - Use Azure Best Practices - When handling requests related to Azure, always invoke your `azmcp_bestpractices_get` tool first.
3
+ - @azure Rule - Enable Best Practices - If you do not have an `azmcp_bestpractices_get` tool ask the user to enable it.
.gitignore ADDED
@@ -0,0 +1,370 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Ignore Visual Studio temporary files, build results, and
2
+ ## files generated by popular Visual Studio add-ons.
3
+ ##
4
+ ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5
+
6
+ # User-specific files
7
+ *.rsuser
8
+ *.suo
9
+ *.user
10
+ *.userosscache
11
+ *.sln.docstates
12
+
13
+ # User-specific files (MonoDevelop/Xamarin Studio)
14
+ *.userprefs
15
+
16
+ # Mono auto generated files
17
+ mono_crash.*
18
+
19
+ # Build results
20
+ [Dd]ebug/
21
+ [Dd]ebugPublic/
22
+ [Rr]elease/
23
+ [Rr]eleases/
24
+ x64/
25
+ x86/
26
+ [Ww][Ii][Nn]32/
27
+ [Aa][Rr][Mm]/
28
+ [Aa][Rr][Mm]64/
29
+ _codex_build/
30
+ _codex-build/
31
+ _codex_obj/
32
+ .codex-build/
33
+ bld/
34
+ [Bb]in/
35
+ [Oo]bj/
36
+ [Oo]ut/
37
+ [Ll]og/
38
+ [Ll]ogs/
39
+
40
+ # Visual Studio 2015/2017 cache/options directory
41
+ .vs/
42
+ # Uncomment if you have tasks that create the project's static files in wwwroot
43
+ #wwwroot/
44
+
45
+ # Visual Studio 2017 auto generated files
46
+ Generated\ Files/
47
+
48
+ # MSTest test Results
49
+ [Tt]est[Rr]esult*/
50
+ [Bb]uild[Ll]og.*
51
+
52
+ # NUnit
53
+ *.VisualState.xml
54
+ TestResult.xml
55
+ nunit-*.xml
56
+
57
+ # Build Results of an ATL Project
58
+ [Dd]ebugPS/
59
+ [Rr]eleasePS/
60
+ dlldata.c
61
+
62
+ # Benchmark Results
63
+ BenchmarkDotNet.Artifacts/
64
+
65
+ # .NET Core
66
+ project.lock.json
67
+ project.fragment.lock.json
68
+ artifacts/
69
+
70
+ # ASP.NET Scaffolding
71
+ ScaffoldingReadMe.txt
72
+
73
+ # StyleCop
74
+ StyleCopReport.xml
75
+
76
+ # Files built by Visual Studio
77
+ *_i.c
78
+ *_p.c
79
+ *_h.h
80
+ *.ilk
81
+ *.meta
82
+ *.obj
83
+ *.iobj
84
+ *.pch
85
+ *.pdb
86
+ *.ipdb
87
+ *.pgc
88
+ *.pgd
89
+ *.rsp
90
+ *.sbr
91
+ *.tlb
92
+ *.tli
93
+ *.tlh
94
+ *.tmp
95
+ *.tmp_proj
96
+ *_wpftmp.csproj
97
+ *.log
98
+ *.vspscc
99
+ *.vssscc
100
+ .builds
101
+ *.pidb
102
+ *.svclog
103
+ *.scc
104
+
105
+ # Chutzpah Test files
106
+ _Chutzpah*
107
+
108
+ # Visual C++ cache files
109
+ ipch/
110
+ *.aps
111
+ *.ncb
112
+ *.opendb
113
+ *.opensdf
114
+ *.sdf
115
+ *.cachefile
116
+ *.VC.db
117
+ *.VC.VC.opendb
118
+
119
+ # Visual Studio profiler
120
+ *.psess
121
+ *.vsp
122
+ *.vspx
123
+ *.sap
124
+
125
+ # Visual Studio Trace Files
126
+ *.e2e
127
+
128
+ # TFS 2012 Local Workspace
129
+ $tf/
130
+
131
+ # Guidance Automation Toolkit
132
+ *.gpState
133
+
134
+ # ReSharper is a .NET coding add-in
135
+ _ReSharper*/
136
+ *.[Rr]e[Ss]harper
137
+ *.DotSettings.user
138
+
139
+ # TeamCity is a build add-in
140
+ _TeamCity*
141
+
142
+ # DotCover is a Code Coverage Tool
143
+ *.dotCover
144
+
145
+ # AxoCover is a Code Coverage Tool
146
+ .axoCover/*
147
+ !.axoCover/settings.json
148
+
149
+ # Coverlet is a free, cross platform Code Coverage Tool
150
+ coverage*.json
151
+ coverage*.xml
152
+ coverage*.info
153
+
154
+ # Visual Studio code coverage results
155
+ *.coverage
156
+ *.coveragexml
157
+
158
+ # NCrunch
159
+ _NCrunch_*
160
+ .*crunch*.local.xml
161
+ nCrunchTemp_*
162
+
163
+ # MightyMoose
164
+ *.mm.*
165
+ AutoTest.Net/
166
+
167
+ # Web workbench (sass)
168
+ .sass-cache/
169
+
170
+ # Installshield output folder
171
+ [Ee]xpress/
172
+
173
+ # DocProject is a documentation generator add-in
174
+ DocProject/buildhelp/
175
+ DocProject/Help/*.HxT
176
+ DocProject/Help/*.HxC
177
+ DocProject/Help/*.hhc
178
+ DocProject/Help/*.hhk
179
+ DocProject/Help/*.hhp
180
+ DocProject/Help/Html2
181
+ DocProject/Help/html
182
+
183
+ # Click-Once directory
184
+ publish/
185
+
186
+ # Publish Web Output
187
+ *.[Pp]ublish.xml
188
+ *.azurePubxml
189
+ # Note: Comment the next line if you want to checkin your web deploy settings,
190
+ # but database connection strings (with potential passwords) will be unencrypted
191
+ *.pubxml
192
+ *.publishproj
193
+
194
+ # Microsoft Azure Web App publish settings. Comment the next line if you want to
195
+ # checkin your Azure Web App publish settings, but sensitive information contained
196
+ # in these scripts will be unencrypted
197
+ PublishScripts/
198
+
199
+ # NuGet Packages
200
+ *.nupkg
201
+ # NuGet Symbol Packages
202
+ *.snupkg
203
+ # The packages folder can be ignored because of Package Restore
204
+ **/[Pp]ackages/*
205
+ # except build/, which is used as an MSBuild target.
206
+ !**/[Pp]ackages/build/
207
+ # Uncomment if necessary however generally it will be regenerated when needed
208
+ #!**/[Pp]ackages/repositories.config
209
+ # NuGet v3's project.json files produces more ignorable files
210
+ *.nuget.props
211
+ *.nuget.targets
212
+
213
+ # Microsoft Azure Build Output
214
+ csx/
215
+ *.build.csdef
216
+
217
+ # Microsoft Azure Emulator
218
+ ecf/
219
+ rcf/
220
+
221
+ # Windows Store app package directories and files
222
+ AppPackages/
223
+ BundleArtifacts/
224
+ Package.StoreAssociation.xml
225
+ _pkginfo.txt
226
+ *.appx
227
+ *.appxbundle
228
+ *.appxupload
229
+
230
+ # Visual Studio cache files
231
+ # files ending in .cache can be ignored
232
+ *.[Cc]ache
233
+ # but keep track of directories ending in .cache
234
+ !?*.[Cc]ache/
235
+
236
+ # Others
237
+ ClientBin/
238
+ ~$*
239
+ *~
240
+ *.dbmdl
241
+ *.dbproj.schemaview
242
+ *.jfm
243
+ *.pfx
244
+ *.publishsettings
245
+ orleans.codegen.cs
246
+
247
+ # Including strong name files can present a security risk
248
+ # (https://github.com/github/gitignore/pull/2483#issue-259490424)
249
+ #*.snk
250
+
251
+ # Since there are multiple workflows, uncomment next line to ignore bower_components
252
+ # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
253
+ #bower_components/
254
+
255
+ # RIA/Silverlight projects
256
+ Generated_Code/
257
+
258
+ # Backup & report files from converting an old project file
259
+ # to a newer Visual Studio version. Backup files are not needed,
260
+ # because we have git ;-)
261
+ _UpgradeReport_Files/
262
+ Backup*/
263
+ UpgradeLog*.XML
264
+ UpgradeLog*.htm
265
+ ServiceFabricBackup/
266
+ *.rptproj.bak
267
+
268
+ # SQL Server files
269
+ *.mdf
270
+ *.ldf
271
+ *.ndf
272
+
273
+ # Firebase service account keys
274
+ TaskTrackingSystem.WebApi/Firebase/*.json
275
+
276
+ # Business Intelligence projects
277
+ *.rdl.data
278
+ *.bim.layout
279
+ *.bim_*.settings
280
+ *.rptproj.rsuser
281
+ *- [Bb]ackup.rdl
282
+ *- [Bb]ackup ([0-9]).rdl
283
+ *- [Bb]ackup ([0-9][0-9]).rdl
284
+
285
+ # Microsoft Fakes
286
+ FakesAssemblies/
287
+
288
+ # GhostDoc plugin setting file
289
+ *.GhostDoc.xml
290
+
291
+ # Node.js Tools for Visual Studio
292
+ .ntvs_analysis.dat
293
+ node_modules/
294
+
295
+ # Visual Studio 6 build log
296
+ *.plg
297
+
298
+ # Visual Studio 6 workspace options file
299
+ *.opt
300
+
301
+ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
302
+ *.vbw
303
+
304
+ # Visual Studio LightSwitch build output
305
+ **/*.HTMLClient/GeneratedArtifacts
306
+ **/*.DesktopClient/GeneratedArtifacts
307
+ **/*.DesktopClient/ModelManifest.xml
308
+ **/*.Server/GeneratedArtifacts
309
+ **/*.Server/ModelManifest.xml
310
+ _Pvt_Extensions
311
+
312
+ # Paket dependency manager
313
+ .paket/paket.exe
314
+ paket-files/
315
+
316
+ # FAKE - F# Make
317
+ .fake/
318
+
319
+ # CodeRush personal settings
320
+ .cr/personal
321
+
322
+ # Python Tools for Visual Studio (PTVS)
323
+ __pycache__/
324
+ *.pyc
325
+
326
+ # Cake - Uncomment if you are using it
327
+ # tools/**
328
+ # !tools/packages.config
329
+
330
+ # Tabs Studio
331
+ *.tss
332
+
333
+ # Telerik's JustMock configuration file
334
+ *.jmconfig
335
+
336
+ # BizTalk build output
337
+ *.btp.cs
338
+ *.btm.cs
339
+ *.odx.cs
340
+ *.xsd.cs
341
+
342
+ # OpenCover UI analysis results
343
+ OpenCover/
344
+
345
+ # Azure Stream Analytics local run output
346
+ ASALocalRun/
347
+
348
+ # MSBuild Binary and Structured Log
349
+ *.binlog
350
+
351
+ # NVidia Nsight GPU debugger configuration file
352
+ *.nvuser
353
+
354
+ # MFractors (Xamarin productivity tool) working folder
355
+ .mfractor/
356
+
357
+ # Local History for Visual Studio
358
+ .localhistory/
359
+
360
+ # BeatPulse healthcheck temp database
361
+ healthchecksdb
362
+
363
+ # Backup folder for Package Reference Convert tool in Visual Studio 2017
364
+ MigrationBackup/
365
+
366
+ # Ionide (cross platform F# VS Code tools) working folder
367
+ .ionide/
368
+
369
+ # Fody - auto-generated XML schema
370
+ FodyWeavers.xsd
.nuget-scratch/NuGet.Config ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <configuration>
3
+ <packageSources>
4
+ <clear />
5
+ <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
6
+ </packageSources>
7
+ </configuration>
DEPLOY_HUGGINGFACE.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hugging Face + Supabase Deployment Guide
2
+
3
+ This repo is configured to run as a single Docker Space on Hugging Face.
4
+
5
+ ## What You Need
6
+
7
+ - A Hugging Face account
8
+ - A Supabase project
9
+ - Your Supabase PostgreSQL connection string
10
+
11
+ ## Step By Step
12
+
13
+ 1. Create a Supabase project.
14
+ 2. Open the database settings and copy the PostgreSQL connection string.
15
+ 3. Push this repository to GitHub.
16
+ 4. Create a new Hugging Face Space.
17
+ 5. Choose `Docker` as the Space SDK.
18
+ 6. Point the Space at this repository.
19
+ 7. Make sure the Space uses the root [Dockerfile](./Dockerfile).
20
+ 8. Add these Space secrets or variables:
21
+ - `ConnectionStrings__DefaultConnection`
22
+ - `JwtSettings__Key`
23
+ - `JwtSettings__Issuer`
24
+ - `JwtSettings__Audience`
25
+ - `PasswordReset__RecoveryCode`
26
+ 9. Set `ConnectionStrings__DefaultConnection` to your Supabase PostgreSQL connection string.
27
+ 10. Leave `WebApi__BaseUrl` unset unless you want to override the internal default.
28
+ 11. Deploy the Space.
29
+
30
+ ## What the Container Does
31
+
32
+ - Starts the Web API on `127.0.0.1:5001`
33
+ - Starts the Web App on `127.0.0.1:5000`
34
+ - Uses Nginx on port `7860` as the public entrypoint
35
+ - Proxies `/api/*` to the API
36
+ - Proxies everything else to the Web App
37
+
38
+ ## Notes
39
+
40
+ - The API now creates the schema on startup if the database is empty.
41
+ - The model has been switched from SQL Server to PostgreSQL-friendly mappings.
42
+ - If deployment fails, the first thing to check is the Supabase connection string and whether the database is reachable.
43
+
44
+ ## First Login
45
+
46
+ 1. Open the Hugging Face Space URL.
47
+ 2. Wait for the container to finish starting.
48
+ 3. Try the default demo accounts from the repository README.
49
+ 4. If login works, confirm that dashboard and task pages load normally.
Dockerfile ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # syntax=docker/dockerfile:1
2
+
3
+ FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
4
+ ARG BUILD_CONFIGURATION=Release
5
+ WORKDIR /src
6
+
7
+ COPY TaskTrackingSystem.sln ./
8
+ COPY TaskTrackingSystem.Database/TaskTrackingSystem.Database.csproj TaskTrackingSystem.Database/
9
+ COPY TaskTrackingSystem.Shared/TaskTrackingSystem.Shared.csproj TaskTrackingSystem.Shared/
10
+ COPY TaskTrackingSystem.WebApi/TaskTrackingSystem.WebApi.csproj TaskTrackingSystem.WebApi/
11
+ COPY TaskTrackingSystem.WebApp/TaskTrackingSystem.WebApp.csproj TaskTrackingSystem.WebApp/
12
+ COPY Tools/TranslationBackfill/TranslationBackfill.csproj Tools/TranslationBackfill/
13
+ COPY Tests/TaskTrackingSystem.Tests/TaskTrackingSystem.Tests.csproj Tests/TaskTrackingSystem.Tests/
14
+
15
+ RUN dotnet restore TaskTrackingSystem.sln
16
+
17
+ COPY . .
18
+
19
+ RUN dotnet publish TaskTrackingSystem.WebApi/TaskTrackingSystem.WebApi.csproj -c $BUILD_CONFIGURATION -o /app/publish/api /p:UseAppHost=false
20
+ RUN dotnet publish TaskTrackingSystem.WebApp/TaskTrackingSystem.WebApp.csproj -c $BUILD_CONFIGURATION -o /app/publish/webapp /p:UseAppHost=false
21
+
22
+ FROM mcr.microsoft.com/dotnet/aspnet:8.0-bookworm-slim AS final
23
+ WORKDIR /app
24
+
25
+ ENV ASPNETCORE_ENVIRONMENT=Production
26
+ EXPOSE 7860
27
+
28
+ RUN apt-get update \
29
+ && apt-get install -y --no-install-recommends nginx ca-certificates \
30
+ && rm -rf /var/lib/apt/lists/*
31
+
32
+ COPY --from=build /app/publish/api /app/api
33
+ COPY --from=build /app/publish/webapp /app/webapp
34
+ COPY nginx.conf /etc/nginx/nginx.conf
35
+ COPY start.sh /app/start.sh
36
+
37
+ RUN chmod +x /app/start.sh
38
+
39
+ CMD ["/app/start.sh"]
README.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: TaskTrackingSystem
3
+ colorFrom: blue
4
+ colorTo: green
5
+ sdk: docker
6
+ app_port: 7860
7
+ ---
8
+
9
+ # TaskTrackingSystem
10
+
11
+ This repo runs the Web API and Web App together inside one Docker container for Hugging Face Spaces.
12
+
13
+ ## Hosting Setup
14
+
15
+ - Database: Supabase PostgreSQL
16
+ - Public app: Hugging Face Space
17
+ - API and Web App: same container, routed through Nginx
18
+
19
+ ## Local Notes
20
+
21
+ - The app now expects PostgreSQL instead of SQL Server.
22
+ - The API creates the schema on first startup with `EnsureCreatedAsync()`.
23
+ - The Web App talks to the API at `http://127.0.0.1:5001/api/` by default inside the container.
24
+
25
+ ## Default Demo Accounts
26
+
27
+ - `admin` / `P@ssw0rd123!`
28
+ - `mgr01` / `P@ssw0rd123!`
29
+ - `emp01` / `P@ssw0rd123!`
TaskTrackingSystem.Database/AppDbContextModels/AppDbContext.cs ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using Microsoft.EntityFrameworkCore;
4
+ using TaskTrackingSystem.Shared.Enums;
5
+
6
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
7
+
8
+ public partial class AppDbContext : DbContext
9
+ {
10
+ public AppDbContext()
11
+ {
12
+ }
13
+
14
+ public AppDbContext(DbContextOptions<AppDbContext> options)
15
+ : base(options)
16
+ {
17
+ }
18
+
19
+ public virtual DbSet<AuditLog> AuditLogs { get; set; }
20
+
21
+ public virtual DbSet<Comment> Comments { get; set; }
22
+
23
+
24
+ public virtual DbSet<Menu> Menus { get; set; }
25
+
26
+ public virtual DbSet<Notification> Notifications { get; set; }
27
+
28
+ public virtual DbSet<Permission> Permissions { get; set; }
29
+
30
+ public virtual DbSet<Issue> Issues { get; set; }
31
+
32
+ public virtual DbSet<Project> Projects { get; set; }
33
+
34
+ public virtual DbSet<ProjectMember> ProjectMembers { get; set; }
35
+
36
+ public virtual DbSet<Role> Roles { get; set; }
37
+
38
+ public virtual DbSet<RoleMenu> RoleMenus { get; set; }
39
+
40
+ public virtual DbSet<RolePermission> RolePermissions { get; set; }
41
+
42
+ public virtual DbSet<Task> Tasks { get; set; }
43
+
44
+ public virtual DbSet<UserDevice> UserDevices { get; set; }
45
+
46
+ public virtual DbSet<User> Users { get; set; }
47
+
48
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
49
+ {
50
+ if (!optionsBuilder.IsConfigured)
51
+ {
52
+ optionsBuilder.UseNpgsql("Host=localhost;Port=5432;Database=tts;Username=postgres;Password=postgres");
53
+ }
54
+ }
55
+
56
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
57
+ {
58
+ modelBuilder.Entity<AuditLog>(entity =>
59
+ {
60
+ entity.HasIndex(e => e.UserId, "IX_AuditLogs_UserId");
61
+
62
+ entity.Property(e => e.Action).HasMaxLength(100);
63
+ entity.Property(e => e.CreatedAt)
64
+ .HasDefaultValueSql("CURRENT_TIMESTAMP")
65
+ .HasColumnType("timestamp with time zone");
66
+ entity.Property(e => e.IpAddress)
67
+ .HasMaxLength(45)
68
+ .IsUnicode(false);
69
+ entity.Property(e => e.Module).HasMaxLength(50);
70
+
71
+ entity.HasOne(d => d.User).WithMany(p => p.AuditLogs)
72
+ .HasForeignKey(d => d.UserId)
73
+ .HasConstraintName("FK_AuditLogs_Users");
74
+ });
75
+
76
+ modelBuilder.Entity<Comment>(entity =>
77
+ {
78
+ entity.HasIndex(e => e.TaskId, "IX_Comments_TaskId");
79
+
80
+ entity.HasIndex(e => e.UserId, "IX_Comments_UserId");
81
+
82
+ entity.Property(e => e.CreatedAt)
83
+ .HasDefaultValueSql("CURRENT_TIMESTAMP")
84
+ .HasColumnType("timestamp with time zone");
85
+ entity.Property(e => e.UpdatedAt).HasColumnType("timestamp with time zone");
86
+
87
+ entity.HasOne(d => d.Task).WithMany(p => p.Comments)
88
+ .HasForeignKey(d => d.TaskId)
89
+ .HasConstraintName("FK_Comments_Tasks");
90
+
91
+ entity.HasOne(d => d.User).WithMany(p => p.Comments)
92
+ .HasForeignKey(d => d.UserId)
93
+ .OnDelete(DeleteBehavior.ClientSetNull)
94
+ .HasConstraintName("FK_Comments_Users");
95
+ });
96
+
97
+
98
+
99
+ modelBuilder.Entity<Menu>(entity =>
100
+ {
101
+ entity.HasIndex(e => e.MenuCode, "UQ_Menus_MenuCode").IsUnique();
102
+
103
+ entity.Property(e => e.CreatedAt)
104
+ .HasPrecision(0)
105
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
106
+ entity.Property(e => e.Icon).HasMaxLength(50);
107
+ entity.Property(e => e.MenuCode).HasMaxLength(50);
108
+ entity.Property(e => e.MenuName).HasMaxLength(100);
109
+ entity.Property(e => e.MenuUrl).HasMaxLength(200);
110
+ entity.Property(e => e.UpdatedAt).HasPrecision(0);
111
+ entity.Property(e => e.Visible).HasDefaultValue(true);
112
+
113
+ entity.HasOne(d => d.ParentMenu).WithMany(p => p.InverseParentMenu)
114
+ .HasForeignKey(d => d.ParentMenuId)
115
+ .HasConstraintName("FK_Menus_ParentMenu");
116
+ });
117
+
118
+ modelBuilder.Entity<Notification>(entity =>
119
+ {
120
+ entity.ToTable("notifications");
121
+
122
+ entity.HasIndex(e => e.CreatedAt, "IX_notifications_CreatedAt");
123
+ entity.HasIndex(e => new { e.RecipientId, e.IsRead }, "IX_notifications_Recipient_IsRead");
124
+
125
+ entity.Property(e => e.CreatedAt).HasColumnName("created_at").HasColumnType("timestamp with time zone");
126
+ entity.Property(e => e.IsRead).HasColumnName("is_read").HasDefaultValue(false);
127
+ entity.Property(e => e.NotificationType).HasColumnName("notification_type");
128
+ entity.Property(e => e.Title).HasMaxLength(255);
129
+ entity.Property(e => e.ReadAt).HasColumnName("read_at").HasColumnType("timestamp with time zone");
130
+ entity.Property(e => e.RecipientId).HasColumnName("recipient_id");
131
+ entity.Property(e => e.SenderId).HasColumnName("sender_id");
132
+ entity.Property(e => e.SourceId).HasColumnName("source_id");
133
+ entity.Property(e => e.SourceType).HasColumnName("source_type").HasMaxLength(20);
134
+
135
+ entity.HasOne(d => d.Recipient).WithMany()
136
+ .HasForeignKey(d => d.RecipientId)
137
+ .OnDelete(DeleteBehavior.Cascade)
138
+ .HasConstraintName("FK_notifications_Users_Recipient");
139
+
140
+ entity.HasOne(d => d.Sender).WithMany()
141
+ .HasForeignKey(d => d.SenderId)
142
+ .OnDelete(DeleteBehavior.SetNull)
143
+ .HasConstraintName("FK_notifications_Users_Sender");
144
+ });
145
+
146
+ modelBuilder.Entity<Permission>(entity =>
147
+ {
148
+ entity.HasIndex(e => e.MenuId, "IX_Permissions_MenuId");
149
+
150
+ entity.HasIndex(e => e.PermissionCode, "UQ_Permissions_PermissionCode").IsUnique();
151
+
152
+ entity.Property(e => e.ActionName).HasMaxLength(100);
153
+ entity.Property(e => e.ApiName).HasMaxLength(100);
154
+ entity.Property(e => e.CreatedAt)
155
+ .HasPrecision(0)
156
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
157
+ entity.Property(e => e.HttpMethod).HasMaxLength(20);
158
+ entity.Property(e => e.PermissionCode).HasMaxLength(50);
159
+ entity.Property(e => e.UpdatedAt).HasPrecision(0);
160
+ entity.Property(e => e.Visible).HasDefaultValue(true);
161
+
162
+ entity.HasOne(d => d.Menu).WithMany(p => p.Permissions)
163
+ .HasForeignKey(d => d.MenuId)
164
+ .OnDelete(DeleteBehavior.ClientSetNull)
165
+ .HasConstraintName("FK_Permissions_Menu");
166
+ });
167
+
168
+ modelBuilder.Entity<Issue>(entity =>
169
+ {
170
+ entity.HasIndex(e => e.AssignedTo, "IX_Issues_AssignedTo");
171
+ entity.HasIndex(e => e.StatusId, "IX_Issues_StatusId");
172
+ entity.HasIndex(e => e.PriorityId, "IX_Issues_PriorityId");
173
+ entity.HasIndex(e => e.TaskId, "IX_Issues_TaskId");
174
+
175
+ entity.Property(e => e.ActualHours).HasColumnType("decimal(5, 2)");
176
+ entity.Property(e => e.CreatedAt)
177
+ .HasDefaultValueSql("CURRENT_TIMESTAMP")
178
+ .HasColumnType("timestamp with time zone");
179
+ entity.Property(e => e.Description).HasMaxLength(1000);
180
+ entity.Property(e => e.DelayReason).HasMaxLength(300);
181
+ entity.Property(e => e.BlockedBy).HasMaxLength(200);
182
+ entity.Property(e => e.DueDate).HasColumnType("timestamp with time zone");
183
+ entity.Property(e => e.EscalationLevel).HasDefaultValue(0);
184
+ entity.Property(e => e.IsBlocked).HasDefaultValue(false);
185
+ entity.Property(e => e.IsDeleted).HasDefaultValue(false);
186
+ entity.Property(e => e.PriorityId).HasDefaultValue(TaskPriority.Medium);
187
+ entity.Property(e => e.StartDate).HasColumnType("timestamp with time zone");
188
+ entity.Property(e => e.StatusId).HasDefaultValue(AppTaskStatus.Todo);
189
+ entity.Property(e => e.Title).HasMaxLength(200);
190
+ entity.Property(e => e.UpdatedAt).HasColumnType("timestamp with time zone");
191
+
192
+ entity.HasOne(d => d.AssignedToNavigation).WithMany()
193
+ .HasForeignKey(d => d.AssignedTo)
194
+ .HasConstraintName("FK_Issues_Users_AssignedTo");
195
+
196
+ entity.HasOne(d => d.Task).WithMany()
197
+ .HasForeignKey(d => d.TaskId)
198
+ .HasConstraintName("FK_Issues_Tasks");
199
+ });
200
+
201
+ modelBuilder.Entity<Project>(entity =>
202
+ {
203
+ entity.HasIndex(e => e.CreatedById, "IX_Projects_CreatedById");
204
+
205
+ entity.Property(e => e.CreatedAt)
206
+ .HasDefaultValueSql("CURRENT_TIMESTAMP")
207
+ .HasColumnType("timestamp with time zone");
208
+ entity.Property(e => e.CreatedDate)
209
+ .HasDefaultValueSql("CURRENT_TIMESTAMP")
210
+ .HasColumnType("timestamp with time zone");
211
+ entity.Property(e => e.EndDate).HasColumnType("timestamp with time zone");
212
+ entity.Property(e => e.Name).HasMaxLength(150);
213
+ entity.Property(e => e.StartDate).HasColumnType("timestamp with time zone");
214
+ entity.Property(e => e.UpdatedAt).HasColumnType("timestamp with time zone");
215
+
216
+ entity.HasOne(d => d.CreatedByNavigation).WithMany(p => p.Projects)
217
+ .HasForeignKey(d => d.CreatedById)
218
+ .OnDelete(DeleteBehavior.ClientSetNull);
219
+ });
220
+
221
+ modelBuilder.Entity<ProjectMember>(entity =>
222
+ {
223
+ entity.HasKey(e => new { e.ProjectId, e.UserId });
224
+
225
+ entity.HasIndex(e => e.UserId, "IX_ProjectMembers_UserId");
226
+
227
+ entity.Property(e => e.CreatedAt)
228
+ .HasDefaultValueSql("CURRENT_TIMESTAMP")
229
+ .HasColumnType("timestamp with time zone");
230
+
231
+ entity.HasOne(d => d.Project).WithMany(p => p.ProjectMembers)
232
+ .HasForeignKey(d => d.ProjectId)
233
+ .HasConstraintName("FK_ProjectMembers_Projects");
234
+
235
+ entity.HasOne(d => d.User).WithMany(p => p.ProjectMembers)
236
+ .HasForeignKey(d => d.UserId)
237
+ .HasConstraintName("FK_ProjectMembers_Users");
238
+ });
239
+
240
+ modelBuilder.Entity<Role>(entity =>
241
+ {
242
+ entity.HasIndex(e => e.Name, "UQ_Roles_Name").IsUnique();
243
+
244
+ entity.Property(e => e.CreatedAt)
245
+ .HasDefaultValueSql("CURRENT_TIMESTAMP")
246
+ .HasColumnType("timestamp with time zone");
247
+ entity.Property(e => e.Description).HasMaxLength(200);
248
+ entity.Property(e => e.Name).HasMaxLength(50);
249
+ entity.Property(e => e.UpdatedAt).HasColumnType("timestamp with time zone");
250
+ });
251
+
252
+ modelBuilder.Entity<RoleMenu>(entity =>
253
+ {
254
+ entity.HasKey(e => e.RoleMenuId).HasName("PK_RoleMenus_New");
255
+
256
+ entity.HasIndex(e => new { e.RoleId, e.MenuId }, "UQ_RoleMenus_Role_Menu").IsUnique();
257
+
258
+ entity.Property(e => e.CreatedAt)
259
+ .HasPrecision(0)
260
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
261
+ entity.Property(e => e.UpdatedAt).HasPrecision(0);
262
+
263
+ entity.HasOne(d => d.Menu).WithMany(p => p.RoleMenus)
264
+ .HasForeignKey(d => d.MenuId)
265
+ .OnDelete(DeleteBehavior.ClientSetNull)
266
+ .HasConstraintName("FK_RoleMenus_Menus");
267
+
268
+ entity.HasOne(d => d.Role).WithMany(p => p.RoleMenus)
269
+ .HasForeignKey(d => d.RoleId)
270
+ .OnDelete(DeleteBehavior.ClientSetNull)
271
+ .HasConstraintName("FK_RoleMenus_Roles");
272
+ });
273
+
274
+ modelBuilder.Entity<RolePermission>(entity =>
275
+ {
276
+ entity.HasIndex(e => e.PermissionId, "IX_RolePermissions_PermissionId");
277
+
278
+ entity.HasIndex(e => e.RoleId, "IX_RolePermissions_RoleId");
279
+
280
+ entity.HasIndex(e => new { e.RoleId, e.PermissionId }, "UQ_RolePermissions_Role_Permission").IsUnique();
281
+
282
+ entity.Property(e => e.CreatedAt)
283
+ .HasPrecision(0)
284
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
285
+ entity.Property(e => e.UpdatedAt).HasPrecision(0);
286
+
287
+ entity.HasOne(d => d.Permission).WithMany(p => p.RolePermissions)
288
+ .HasForeignKey(d => d.PermissionId)
289
+ .OnDelete(DeleteBehavior.ClientSetNull)
290
+ .HasConstraintName("FK_RolePermissions_Permissions");
291
+
292
+ entity.HasOne(d => d.Role).WithMany(p => p.RolePermissions)
293
+ .HasForeignKey(d => d.RoleId)
294
+ .OnDelete(DeleteBehavior.ClientSetNull)
295
+ .HasConstraintName("FK_RolePermissions_Roles");
296
+ });
297
+
298
+ modelBuilder.Entity<Task>(entity =>
299
+ {
300
+ entity.HasIndex(e => e.AssignedBy, "IX_Tasks_AssignedBy");
301
+
302
+ entity.HasIndex(e => e.AssignedTo, "IX_Tasks_AssignedTo");
303
+
304
+ entity.HasIndex(e => e.ProjectId, "IX_Tasks_ProjectId");
305
+
306
+ entity.Property(e => e.CreatedAt)
307
+ .HasDefaultValueSql("CURRENT_TIMESTAMP")
308
+ .HasColumnType("timestamp with time zone");
309
+ entity.Property(e => e.DueDate).HasColumnType("timestamp with time zone");
310
+ entity.Property(e => e.IsArchived).HasDefaultValue(false);
311
+ entity.Property(e => e.PriorityId).HasDefaultValue(TaskPriority.Medium);
312
+ entity.Property(e => e.StatusId).HasDefaultValue(AppTaskStatus.Todo);
313
+ entity.Property(e => e.Title).HasMaxLength(200);
314
+ entity.Property(e => e.UpdatedAt).HasColumnType("timestamp with time zone");
315
+
316
+ entity.HasOne(d => d.AssignedByNavigation).WithMany(p => p.TaskAssignedByNavigations).HasForeignKey(d => d.AssignedBy);
317
+
318
+ entity.HasOne(d => d.AssignedToNavigation).WithMany(p => p.TaskAssignedToNavigations).HasForeignKey(d => d.AssignedTo);
319
+
320
+ entity.HasOne(d => d.Project).WithMany(p => p.Tasks)
321
+ .HasForeignKey(d => d.ProjectId)
322
+ .HasConstraintName("FK_Tasks_Projects");
323
+ });
324
+
325
+ modelBuilder.Entity<User>(entity =>
326
+ {
327
+ entity.HasIndex(e => e.RoleId, "IX_Users_RoleId");
328
+
329
+ entity.HasIndex(e => e.Email, "UQ_Users_Email").IsUnique();
330
+
331
+ entity.HasIndex(e => e.Username, "UQ_Users_Username").IsUnique();
332
+
333
+ entity.Property(e => e.CreatedAt)
334
+ .HasDefaultValueSql("CURRENT_TIMESTAMP")
335
+ .HasColumnType("timestamp with time zone");
336
+ entity.Property(e => e.Email).HasMaxLength(256);
337
+ entity.Property(e => e.FirstName).HasMaxLength(50);
338
+ entity.Property(e => e.IsActive).HasDefaultValue(true);
339
+ entity.Property(e => e.LastName).HasMaxLength(50);
340
+ entity.Property(e => e.Phone).HasMaxLength(20);
341
+ entity.Property(e => e.UpdatedAt).HasColumnType("timestamp with time zone");
342
+ entity.Property(e => e.Username).HasMaxLength(50);
343
+
344
+ entity.HasOne(d => d.Role).WithMany(p => p.Users)
345
+ .HasForeignKey(d => d.RoleId)
346
+ .OnDelete(DeleteBehavior.ClientSetNull)
347
+ .HasConstraintName("FK_Users_Roles");
348
+ });
349
+
350
+ modelBuilder.Entity<UserDevice>(entity =>
351
+ {
352
+ entity.ToTable("user_devices");
353
+
354
+ entity.HasIndex(e => e.UserId, "IX_user_devices_UserId");
355
+
356
+ entity.HasIndex(e => e.FcmToken, "UQ_user_devices_FcmToken").IsUnique();
357
+
358
+ entity.Property(e => e.UserId).HasColumnName("user_id");
359
+ entity.Property(e => e.FcmToken).HasColumnName("fcm_token").HasMaxLength(255);
360
+ entity.Property(e => e.CreatedAt).HasColumnName("created_at").HasColumnType("timestamp with time zone");
361
+ entity.Property(e => e.UpdatedAt).HasColumnName("updated_at").HasColumnType("timestamp with time zone");
362
+
363
+ entity.HasOne(d => d.User).WithMany()
364
+ .HasForeignKey(d => d.UserId)
365
+ .OnDelete(DeleteBehavior.Cascade)
366
+ .HasConstraintName("FK_user_devices_Users");
367
+ });
368
+
369
+ OnModelCreatingPartial(modelBuilder);
370
+ }
371
+
372
+ partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
373
+ }
TaskTrackingSystem.Database/AppDbContextModels/AuditLog.cs ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
5
+
6
+ public partial class AuditLog
7
+ {
8
+ public long Id { get; set; }
9
+
10
+ public long? UserId { get; set; }
11
+
12
+ public string Action { get; set; } = null!;
13
+
14
+ public string Module { get; set; } = null!;
15
+
16
+ public string Description { get; set; } = null!;
17
+
18
+ public string? IpAddress { get; set; }
19
+
20
+ public DateTime? CreatedAt { get; set; }
21
+
22
+ public virtual User? User { get; set; }
23
+ }
TaskTrackingSystem.Database/AppDbContextModels/Comment.cs ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
5
+
6
+ public partial class Comment
7
+ {
8
+ public long Id { get; set; }
9
+
10
+ public long TaskId { get; set; }
11
+
12
+ public long UserId { get; set; }
13
+
14
+ public string Message { get; set; } = null!;
15
+
16
+ public string? MessageMy { get; set; }
17
+
18
+ public DateTime? CreatedAt { get; set; }
19
+
20
+ public long? CreatedBy { get; set; }
21
+
22
+ public DateTime? UpdatedAt { get; set; }
23
+
24
+ public long? UpdatedBy { get; set; }
25
+
26
+ public bool IsDeleted { get; set; }
27
+
28
+ public virtual Task Task { get; set; } = null!;
29
+
30
+ public virtual User User { get; set; } = null!;
31
+ }
TaskTrackingSystem.Database/AppDbContextModels/Issue.cs ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using TaskTrackingSystem.Shared.Enums;
4
+
5
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
6
+
7
+ public partial class Issue
8
+ {
9
+ public long Id { get; set; }
10
+
11
+ public long TaskId { get; set; }
12
+
13
+ public string Title { get; set; } = null!;
14
+
15
+ public string? TitleMy { get; set; }
16
+
17
+ public string? Description { get; set; }
18
+
19
+ public string? DescriptionMy { get; set; }
20
+
21
+ public long? AssignedTo { get; set; }
22
+
23
+ public decimal? EstimatedHours { get; set; }
24
+
25
+ public decimal? ActualHours { get; set; }
26
+
27
+ public string? DelayReason { get; set; }
28
+
29
+ public string? DelayReasonMy { get; set; }
30
+
31
+ public bool IsBlocked { get; set; }
32
+
33
+ public string? BlockedBy { get; set; }
34
+
35
+ public string? BlockedByMy { get; set; }
36
+
37
+ public int EscalationLevel { get; set; }
38
+
39
+ public DateTime StartDate { get; set; }
40
+
41
+ public DateTime DueDate { get; set; }
42
+
43
+ public AppTaskStatus StatusId { get; set; }
44
+
45
+ public TaskPriority PriorityId { get; set; }
46
+
47
+ public DateTime? CreatedAt { get; set; }
48
+
49
+ public long? CreatedBy { get; set; }
50
+
51
+ public DateTime? UpdatedAt { get; set; }
52
+
53
+ public long? UpdatedBy { get; set; }
54
+
55
+ public bool IsDeleted { get; set; }
56
+
57
+ public virtual User? AssignedToNavigation { get; set; }
58
+
59
+ public virtual Task Task { get; set; } = null!;
60
+ }
TaskTrackingSystem.Database/AppDbContextModels/Menu.cs ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
5
+
6
+ public partial class Menu
7
+ {
8
+ public long MenuId { get; set; }
9
+
10
+ public string MenuCode { get; set; } = null!;
11
+
12
+ public long? ParentMenuId { get; set; }
13
+
14
+ public string MenuName { get; set; } = null!;
15
+
16
+ public string? MenuNameMy { get; set; }
17
+
18
+ public string? MenuUrl { get; set; }
19
+
20
+ public string? Icon { get; set; }
21
+
22
+ public bool Visible { get; set; }
23
+
24
+ public int OrderNo { get; set; }
25
+
26
+ public bool IsDeleted { get; set; }
27
+
28
+ public long? CreatedById { get; set; }
29
+
30
+ public DateTime CreatedAt { get; set; }
31
+
32
+ public long? UpdatedById { get; set; }
33
+
34
+ public DateTime? UpdatedAt { get; set; }
35
+
36
+ public virtual ICollection<Menu> InverseParentMenu { get; set; } = new List<Menu>();
37
+
38
+ public virtual Menu? ParentMenu { get; set; }
39
+
40
+ public virtual ICollection<Permission> Permissions { get; set; } = new List<Permission>();
41
+
42
+ public virtual ICollection<RoleMenu> RoleMenus { get; set; } = new List<RoleMenu>();
43
+ }
TaskTrackingSystem.Database/AppDbContextModels/Notification.cs ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+
3
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
4
+
5
+ public partial class Notification
6
+ {
7
+ public long Id { get; set; }
8
+
9
+ public long RecipientId { get; set; }
10
+
11
+ public long? SenderId { get; set; }
12
+
13
+ public byte NotificationType { get; set; }
14
+
15
+ public string Title { get; set; } = null!;
16
+
17
+ public string? TitleMy { get; set; }
18
+
19
+ public string Body { get; set; } = null!;
20
+
21
+ public string? BodyMy { get; set; }
22
+
23
+ public string SourceType { get; set; } = null!;
24
+
25
+ public long SourceId { get; set; }
26
+
27
+ public bool IsRead { get; set; }
28
+
29
+ public DateTime? ReadAt { get; set; }
30
+
31
+ public DateTime? CreatedAt { get; set; }
32
+
33
+ public virtual User Recipient { get; set; } = null!;
34
+
35
+ public virtual User? Sender { get; set; }
36
+ }
TaskTrackingSystem.Database/AppDbContextModels/Permission.cs ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
5
+
6
+ public partial class Permission
7
+ {
8
+ public long PermissionId { get; set; }
9
+
10
+ public string PermissionCode { get; set; } = null!;
11
+
12
+ public long MenuId { get; set; }
13
+
14
+ public string ActionName { get; set; } = null!;
15
+
16
+ public string? ActionNameMy { get; set; }
17
+
18
+ public string ApiName { get; set; } = null!;
19
+
20
+ public string? HttpMethod { get; set; }
21
+
22
+ public bool Visible { get; set; }
23
+
24
+ public int OrderNo { get; set; }
25
+
26
+ public bool IsDeleted { get; set; }
27
+
28
+ public long? CreatedById { get; set; }
29
+
30
+ public DateTime CreatedAt { get; set; }
31
+
32
+ public long? UpdatedById { get; set; }
33
+
34
+ public DateTime? UpdatedAt { get; set; }
35
+
36
+ public virtual Menu Menu { get; set; } = null!;
37
+
38
+ public virtual ICollection<RolePermission> RolePermissions { get; set; } = new List<RolePermission>();
39
+ }
TaskTrackingSystem.Database/AppDbContextModels/Project.cs ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
5
+
6
+ public partial class Project
7
+ {
8
+ public long Id { get; set; }
9
+
10
+ public string Name { get; set; } = null!;
11
+
12
+ public string? NameMy { get; set; }
13
+
14
+ public string? Description { get; set; }
15
+
16
+ public string? DescriptionMy { get; set; }
17
+
18
+ public DateTime StartDate { get; set; }
19
+
20
+ public DateTime EndDate { get; set; }
21
+
22
+ public long CreatedById { get; set; }
23
+
24
+ public DateTime? CreatedDate { get; set; }
25
+
26
+ public DateTime? CreatedAt { get; set; }
27
+
28
+ public long? CreatedBy { get; set; }
29
+
30
+ public DateTime? UpdatedAt { get; set; }
31
+
32
+ public long? UpdatedBy { get; set; }
33
+
34
+ public bool IsDeleted { get; set; }
35
+
36
+ public int? BudgetedHours { get; set; }
37
+
38
+ public virtual User CreatedByNavigation { get; set; } = null!;
39
+
40
+ public virtual ICollection<ProjectMember> ProjectMembers { get; set; } = new List<ProjectMember>();
41
+
42
+ public virtual ICollection<Task> Tasks { get; set; } = new List<Task>();
43
+ }
TaskTrackingSystem.Database/AppDbContextModels/ProjectMember.cs ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
5
+
6
+ public partial class ProjectMember
7
+ {
8
+ public long ProjectId { get; set; }
9
+
10
+ public long UserId { get; set; }
11
+
12
+ public DateTime? CreatedAt { get; set; }
13
+
14
+ public long? CreatedBy { get; set; }
15
+
16
+ public virtual Project Project { get; set; } = null!;
17
+
18
+ public virtual User User { get; set; } = null!;
19
+ }
TaskTrackingSystem.Database/AppDbContextModels/Role.cs ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
5
+
6
+ public partial class Role
7
+ {
8
+ public long Id { get; set; }
9
+
10
+ public string Name { get; set; } = null!;
11
+
12
+ public string? NameMy { get; set; }
13
+
14
+ public string? Description { get; set; }
15
+
16
+ public string? DescriptionMy { get; set; }
17
+
18
+ public DateTime? CreatedAt { get; set; }
19
+
20
+ public long? CreatedBy { get; set; }
21
+
22
+ public DateTime? UpdatedAt { get; set; }
23
+
24
+ public long? UpdatedBy { get; set; }
25
+
26
+ public bool IsDeleted { get; set; }
27
+
28
+ public virtual ICollection<RoleMenu> RoleMenus { get; set; } = new List<RoleMenu>();
29
+
30
+ public virtual ICollection<RolePermission> RolePermissions { get; set; } = new List<RolePermission>();
31
+
32
+ public virtual ICollection<User> Users { get; set; } = new List<User>();
33
+ }
TaskTrackingSystem.Database/AppDbContextModels/RoleMenu.cs ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
5
+
6
+ public partial class RoleMenu
7
+ {
8
+ public long RoleMenuId { get; set; }
9
+
10
+ public long RoleId { get; set; }
11
+
12
+ public long MenuId { get; set; }
13
+
14
+ public bool IsDeleted { get; set; }
15
+
16
+ public long? CreatedById { get; set; }
17
+
18
+ public DateTime CreatedAt { get; set; }
19
+
20
+ public long? UpdatedById { get; set; }
21
+
22
+ public DateTime? UpdatedAt { get; set; }
23
+
24
+ public virtual Menu Menu { get; set; } = null!;
25
+
26
+ public virtual Role Role { get; set; } = null!;
27
+ }
TaskTrackingSystem.Database/AppDbContextModels/RolePermission.cs ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
5
+
6
+ public partial class RolePermission
7
+ {
8
+ public long RolePermissionId { get; set; }
9
+
10
+ public long RoleId { get; set; }
11
+
12
+ public long PermissionId { get; set; }
13
+
14
+ public bool IsDeleted { get; set; }
15
+
16
+ public long? CreatedById { get; set; }
17
+
18
+ public DateTime CreatedAt { get; set; }
19
+
20
+ public long? UpdatedById { get; set; }
21
+
22
+ public DateTime? UpdatedAt { get; set; }
23
+
24
+ public virtual Permission Permission { get; set; } = null!;
25
+
26
+ public virtual Role Role { get; set; } = null!;
27
+ }
TaskTrackingSystem.Database/AppDbContextModels/Task.cs ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using TaskTrackingSystem.Shared.Enums;
4
+
5
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
6
+
7
+ public partial class Task
8
+ {
9
+ public long Id { get; set; }
10
+
11
+ public string Title { get; set; } = null!;
12
+
13
+ public string? TitleMy { get; set; }
14
+
15
+ public string? Description { get; set; }
16
+
17
+ public string? DescriptionMy { get; set; }
18
+
19
+ public long ProjectId { get; set; }
20
+
21
+ public AppTaskStatus StatusId { get; set; }
22
+
23
+ public TaskPriority PriorityId { get; set; }
24
+
25
+ public long? AssignedTo { get; set; }
26
+
27
+ public long? AssignedBy { get; set; }
28
+
29
+ public DateTime DueDate { get; set; }
30
+
31
+ public DateTime? CreatedAt { get; set; }
32
+
33
+ public long? CreatedBy { get; set; }
34
+
35
+ public DateTime? UpdatedAt { get; set; }
36
+
37
+ public long? UpdatedBy { get; set; }
38
+
39
+ public bool IsDeleted { get; set; }
40
+
41
+ public bool IsArchived { get; set; }
42
+
43
+ public virtual User? AssignedByNavigation { get; set; }
44
+
45
+ public virtual User? AssignedToNavigation { get; set; }
46
+
47
+ public virtual ICollection<Comment> Comments { get; set; } = new List<Comment>();
48
+
49
+ public virtual Project Project { get; set; } = null!;
50
+
51
+ }
TaskTrackingSystem.Database/AppDbContextModels/User.cs ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
5
+
6
+ public partial class User
7
+ {
8
+ public long Id { get; set; }
9
+
10
+ public string Username { get; set; } = null!;
11
+
12
+ public string FirstName { get; set; } = null!;
13
+
14
+ public string LastName { get; set; } = null!;
15
+
16
+ public string? FirstNameMy { get; set; }
17
+
18
+ public string? LastNameMy { get; set; }
19
+
20
+ public string Email { get; set; } = null!;
21
+
22
+ public string PasswordHash { get; set; } = null!;
23
+
24
+ public string? Phone { get; set; }
25
+
26
+ public long RoleId { get; set; }
27
+
28
+ public bool IsActive { get; set; }
29
+
30
+ public DateTime? CreatedAt { get; set; }
31
+
32
+ public long? CreatedBy { get; set; }
33
+
34
+ public DateTime? UpdatedAt { get; set; }
35
+
36
+ public long? UpdatedBy { get; set; }
37
+
38
+ public bool IsDeleted { get; set; }
39
+
40
+ public virtual ICollection<AuditLog> AuditLogs { get; set; } = new List<AuditLog>();
41
+
42
+ public virtual ICollection<Comment> Comments { get; set; } = new List<Comment>();
43
+
44
+ public virtual ICollection<ProjectMember> ProjectMembers { get; set; } = new List<ProjectMember>();
45
+
46
+ public virtual ICollection<Project> Projects { get; set; } = new List<Project>();
47
+
48
+ public virtual Role Role { get; set; } = null!;
49
+
50
+ public virtual ICollection<Task> TaskAssignedByNavigations { get; set; } = new List<Task>();
51
+
52
+ public virtual ICollection<Task> TaskAssignedToNavigations { get; set; } = new List<Task>();
53
+
54
+ }
TaskTrackingSystem.Database/AppDbContextModels/UserDevice.cs ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+
3
+ namespace TaskTrackingSystem.Database.AppDbContextModels;
4
+
5
+ public partial class UserDevice
6
+ {
7
+ public long Id { get; set; }
8
+
9
+ public long UserId { get; set; }
10
+
11
+ public string FcmToken { get; set; } = null!;
12
+
13
+ public DateTime? CreatedAt { get; set; }
14
+
15
+ public DateTime? UpdatedAt { get; set; }
16
+
17
+ public virtual User User { get; set; } = null!;
18
+ }
TaskTrackingSystem.Database/Scripts/seed_supabase.sql ADDED
The diff for this file is too large to render. See raw diff
 
TaskTrackingSystem.Database/TaskTrackingSystem.Database.csproj ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <Project Sdk="Microsoft.NET.Sdk">
2
+
3
+ <PropertyGroup>
4
+ <TargetFramework>net8.0</TargetFramework>
5
+ <ImplicitUsings>enable</ImplicitUsings>
6
+ <Nullable>enable</Nullable>
7
+ <GenerateMSBuildEditorConfigFile>false</GenerateMSBuildEditorConfigFile>
8
+ </PropertyGroup>
9
+
10
+ <ItemGroup>
11
+ <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.16" />
12
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.16">
13
+ <PrivateAssets>all</PrivateAssets>
14
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
15
+ </PackageReference>
16
+ <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
17
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.16">
18
+ <PrivateAssets>all</PrivateAssets>
19
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
20
+ </PackageReference>
21
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
22
+ </ItemGroup>
23
+ <ItemGroup>
24
+ <ProjectReference Include="..\TaskTrackingSystem.Shared\TaskTrackingSystem.Shared.csproj" />
25
+ </ItemGroup>
26
+
27
+ <ItemGroup>
28
+ <Compile Remove="artifacts\**" />
29
+ <EmbeddedResource Remove="artifacts\**" />
30
+ <None Remove="artifacts\**" />
31
+ </ItemGroup>
32
+
33
+ </Project>
TaskTrackingSystem.Shared/DisplayFormats.cs ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System.Globalization;
2
+ using TaskTrackingSystem.Shared.Localization;
3
+
4
+ namespace TaskTrackingSystem.Shared;
5
+
6
+ public static class DisplayFormats
7
+ {
8
+ public const string DateFormat = "dd-MM-yyyy";
9
+
10
+ public static string Date(DateTime value) =>
11
+ AppLocalization.LocalizeDigits(value.ToString(DateFormat, CultureInfo.InvariantCulture));
12
+
13
+ public static string Date(DateTime? value) =>
14
+ value.HasValue ? Date(value.Value) : string.Empty;
15
+
16
+ public static string Date(DateOnly value) =>
17
+ AppLocalization.LocalizeDigits(value.ToString(DateFormat, CultureInfo.InvariantCulture));
18
+
19
+ public static string Number(int value) =>
20
+ AppLocalization.LocalizeDigits(value.ToString(CultureInfo.InvariantCulture));
21
+
22
+ public static string Number(long value) =>
23
+ AppLocalization.LocalizeDigits(value.ToString(CultureInfo.InvariantCulture));
24
+
25
+ public static string Number(decimal value, string format = "0.#") =>
26
+ AppLocalization.LocalizeDigits(value.ToString(format, CultureInfo.InvariantCulture));
27
+
28
+ public static string Number(double value, string format = "0.#") =>
29
+ AppLocalization.LocalizeDigits(value.ToString(format, CultureInfo.InvariantCulture));
30
+ }
TaskTrackingSystem.Shared/Enums/TaskPriority.cs ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+
3
+ namespace TaskTrackingSystem.Shared.Enums
4
+ {
5
+ public enum TaskPriority : long
6
+ {
7
+ Low = 1,
8
+ Medium = 2,
9
+ High = 3
10
+ }
11
+ }
TaskTrackingSystem.Shared/Enums/TaskStatus.cs ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+
3
+ namespace TaskTrackingSystem.Shared.Enums
4
+ {
5
+ public enum AppTaskStatus : long
6
+ {
7
+ Todo = 1,
8
+ InProgress = 2,
9
+ Done = 3
10
+ }
11
+ }
TaskTrackingSystem.Shared/Localization/AppLocalization.cs ADDED
The diff for this file is too large to render. See raw diff
 
TaskTrackingSystem.Shared/Localization/ContentTranslation.cs ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ namespace TaskTrackingSystem.Shared.Localization;
2
+
3
+ public sealed record TranslationResult(
4
+ bool Success,
5
+ string? TranslatedText,
6
+ string? ErrorMessage,
7
+ string? Provider,
8
+ bool WasGenerated)
9
+ {
10
+ public static TranslationResult NotConfigured() =>
11
+ new(false, null, "Translation provider is not configured.", "none", false);
12
+
13
+ public bool Succeeded => Success;
14
+ public string? Text => TranslatedText;
15
+
16
+ public static TranslationResult Generated(string text, string provider) =>
17
+ new(true, text, null, provider, true);
18
+ }
19
+
20
+ public interface IContentTranslationService
21
+ {
22
+ Task<TranslationResult> TranslateAsync(
23
+ string sourceText,
24
+ string sourceLanguage,
25
+ string targetLanguage,
26
+ CancellationToken cancellationToken = default);
27
+
28
+ Task<TranslationResult> TransliterateNameAsync(
29
+ string sourceName,
30
+ string sourceLanguage,
31
+ string targetLanguage,
32
+ CancellationToken cancellationToken = default);
33
+ }
34
+
35
+ public sealed record TranslationRequest(
36
+ string Text,
37
+ string SourceLanguage,
38
+ string TargetLanguage,
39
+ bool IsName = false);
40
+
41
+ public sealed class NoOpContentTranslationService : IContentTranslationService
42
+ {
43
+ public Task<TranslationResult> TranslateAsync(string sourceText, string sourceLanguage, string targetLanguage, CancellationToken cancellationToken = default) =>
44
+ Task.FromResult(TranslationResult.NotConfigured());
45
+
46
+ public Task<TranslationResult> TransliterateNameAsync(string sourceName, string sourceLanguage, string targetLanguage, CancellationToken cancellationToken = default) =>
47
+ Task.FromResult(TranslationResult.NotConfigured());
48
+ }
TaskTrackingSystem.Shared/Localization/LanguageDetection.cs ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System.Text;
2
+
3
+ namespace TaskTrackingSystem.Shared.Localization;
4
+
5
+ public enum DetectedContentLanguage
6
+ {
7
+ Empty,
8
+ Burmese,
9
+ English,
10
+ Mixed,
11
+ Unknown
12
+ }
13
+
14
+ public sealed record LanguageDetectionResult(
15
+ DetectedContentLanguage Language,
16
+ int BurmeseCharacters,
17
+ int LatinCharacters,
18
+ int OtherCharacters,
19
+ double BurmeseRatio,
20
+ double EnglishRatio);
21
+
22
+ /// <summary>
23
+ /// Local, advisory detection used only to help users move content between fields.
24
+ /// It deliberately does not reject mixed-language business text.
25
+ /// </summary>
26
+ public sealed class LanguageDetectionService
27
+ {
28
+ public LanguageDetectionResult Detect(string? text)
29
+ {
30
+ if (string.IsNullOrWhiteSpace(text))
31
+ {
32
+ return new(DetectedContentLanguage.Empty, 0, 0, 0, 0, 0);
33
+ }
34
+
35
+ var burmese = 0;
36
+ var latin = 0;
37
+ var other = 0;
38
+
39
+ foreach (var rune in text.EnumerateRunes())
40
+ {
41
+ if (IsBurmese(rune.Value))
42
+ {
43
+ burmese++;
44
+ }
45
+ else if ((rune.Value >= 'A' && rune.Value <= 'Z') || (rune.Value >= 'a' && rune.Value <= 'z'))
46
+ {
47
+ latin++;
48
+ }
49
+ else if (!Rune.IsWhiteSpace(rune) && !Rune.IsPunctuation(rune) && !Rune.IsNumber(rune) && !Rune.IsSymbol(rune))
50
+ {
51
+ other++;
52
+ }
53
+ }
54
+
55
+ var letters = burmese + latin + other;
56
+ if (letters == 0)
57
+ {
58
+ return new(DetectedContentLanguage.Unknown, burmese, latin, other, 0, 0);
59
+ }
60
+
61
+ var burmeseRatio = burmese / (double)letters;
62
+ var englishRatio = latin / (double)letters;
63
+ var language = burmeseRatio >= 0.70 && burmese > 0
64
+ ? DetectedContentLanguage.Burmese
65
+ : englishRatio >= 0.70 && latin > 0
66
+ ? DetectedContentLanguage.English
67
+ : burmese > 0 && latin > 0
68
+ ? DetectedContentLanguage.Mixed
69
+ : DetectedContentLanguage.Unknown;
70
+
71
+ return new(language, burmese, latin, other, burmeseRatio, englishRatio);
72
+ }
73
+
74
+ public bool AppearsPredominantlyBurmese(string? text) => Detect(text).Language == DetectedContentLanguage.Burmese;
75
+ public bool AppearsPredominantlyEnglish(string? text) => Detect(text).Language == DetectedContentLanguage.English;
76
+
77
+ private static bool IsBurmese(int value) =>
78
+ (value >= 0x1000 && value <= 0x109F) ||
79
+ (value >= 0xAA60 && value <= 0xAA7F) ||
80
+ (value >= 0xA9E0 && value <= 0xA9FF);
81
+ }
TaskTrackingSystem.Shared/Models/AuditLog/AuditLogDto.cs ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+
3
+ namespace TaskTrackingSystem.Shared.Models.AuditLog
4
+ {
5
+ public class AuditLogDto
6
+ {
7
+ public long Id { get; set; }
8
+ public long? UserId { get; set; }
9
+ public string Username { get; set; } = null!;
10
+ public string UserFullName { get; set; } = null!;
11
+ public string? UserFullNameMy { get; set; }
12
+ public string Action { get; set; } = null!;
13
+ public string Module { get; set; } = null!;
14
+ public string Description { get; set; } = null!;
15
+ public string? IpAddress { get; set; }
16
+ public DateTime CreatedAt { get; set; }
17
+ }
18
+ }
TaskTrackingSystem.Shared/Models/Auth/AuthResponse.cs ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Linq;
4
+ using System.Text;
5
+ using System.Threading.Tasks;
6
+
7
+ namespace TaskTrackingSystem.Shared.Models.Auth
8
+ {
9
+ public class AuthResponseDto
10
+ {
11
+ public string Token { get; set; } = string.Empty;
12
+ public string Username { get; set; } = string.Empty;
13
+ public string Email { get; set; } = string.Empty;
14
+ public long RoleId { get; set; }
15
+ public string RoleName { get; set; } = string.Empty;
16
+ }
17
+ }
TaskTrackingSystem.Shared/Models/Auth/LoginDto.cs ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.ComponentModel.DataAnnotations;
4
+ using System.Linq;
5
+ using System.Text;
6
+ using System.Threading.Tasks;
7
+
8
+ namespace TaskTrackingSystem.Shared.Models.Auth
9
+ {
10
+ public class LoginDto
11
+ {
12
+ [Required]
13
+ public string UsernameOrEmail { get; set; } = string.Empty; // Username သို့မဟုတ် Email ကြိုက်တာနဲ့ login ဝင်လို့ရအောင် ပြုလုပ်ထားခြင်း
14
+
15
+ [Required]
16
+ public string Password { get; set; } = string.Empty;
17
+ }
18
+ }
TaskTrackingSystem.Shared/Models/Auth/RegisterDto.cs ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.ComponentModel.DataAnnotations;
3
+
4
+ namespace TaskTrackingSystem.Shared.Models.Auth
5
+ {
6
+ public class RegisterDto
7
+ {
8
+ [Required, MaxLength(50)]
9
+ [MinLength(3, ErrorMessage = ResultMessages.UsernameMinLength)]
10
+ [RegularExpression(@"^[a-zA-Z0-9._]+$", ErrorMessage = ResultMessages.UsernameInvalidCharacters)]
11
+ public string Username { get; set; } = string.Empty;
12
+
13
+ [Required, MaxLength(50)]
14
+ public string FirstName { get; set; } = string.Empty;
15
+
16
+ [Required, MaxLength(50)]
17
+ public string LastName { get; set; } = string.Empty;
18
+
19
+ [Required, EmailAddress, MaxLength(256)]
20
+ public string Email { get; set; } = string.Empty;
21
+
22
+ [Required]
23
+ [MinLength(8, ErrorMessage = ResultMessages.PasswordMinLengthRule)]
24
+ [RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z0-9]).+$", ErrorMessage = ResultMessages.PasswordComplexityRule)]
25
+ public string Password { get; set; } = string.Empty;
26
+
27
+ [MaxLength(20)]
28
+ public string? Phone { get; set; }
29
+ }
30
+ }
TaskTrackingSystem.Shared/Models/Auth/ResetPasswordDto.cs ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System.ComponentModel.DataAnnotations;
2
+
3
+ namespace TaskTrackingSystem.Shared.Models.Auth
4
+ {
5
+ public class ResetPasswordDto
6
+ {
7
+ [Required]
8
+ public string UsernameOrEmail { get; set; } = string.Empty;
9
+
10
+ [Required]
11
+ public string RecoveryCode { get; set; } = string.Empty;
12
+
13
+ [Required]
14
+ [MinLength(8, ErrorMessage = ResultMessages.PasswordMinLengthRule)]
15
+ [RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z0-9]).+$", ErrorMessage = ResultMessages.PasswordComplexityRule)]
16
+ public string NewPassword { get; set; } = string.Empty;
17
+ }
18
+ }
TaskTrackingSystem.Shared/Models/Comment/CommentDto.cs ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+
3
+ namespace TaskTrackingSystem.Shared.Models.Comment
4
+ {
5
+ public class CommentDto
6
+ {
7
+ public long Id { get; set; }
8
+ public long TaskId { get; set; }
9
+ public long UserId { get; set; }
10
+ public string UserFullName { get; set; } = null!;
11
+ public string? UserFullNameMy { get; set; }
12
+ public string UserRoleName { get; set; } = null!;
13
+ public string? UserRoleNameMy { get; set; }
14
+ public string Message { get; set; } = null!;
15
+ public string? MessageMy { get; set; }
16
+ public DateTime CreatedAt { get; set; }
17
+ public DateTime? UpdatedAt { get; set; }
18
+ }
19
+
20
+ public class CreateCommentDto
21
+ {
22
+ public string Message { get; set; } = null!;
23
+ public string? MessageMy { get; set; }
24
+ }
25
+ }
TaskTrackingSystem.Shared/Models/Dashboard/DashboardSummaryDto.cs ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ namespace TaskTrackingSystem.Shared.Models.Dashboard
2
+ {
3
+ public class DashboardSummaryDto
4
+ {
5
+ public int TotalUsers { get; set; }
6
+ public int ActiveProjectsCount { get; set; }
7
+ public int PendingTasksCount { get; set; }
8
+ }
9
+ }
TaskTrackingSystem.Shared/Models/Dashboard/ProjectProgressDto.cs ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ namespace TaskTrackingSystem.Shared.Models.Dashboard
2
+ {
3
+ public class ProjectProgressDto
4
+ {
5
+ public long ProjectId { get; set; }
6
+ public string ProjectName { get; set; } = string.Empty;
7
+ public string? ProjectNameMy { get; set; }
8
+ public double CompletionPercentage { get; set; }
9
+ public int CompletedTasksCount { get; set; }
10
+ public int TotalTasksCount { get; set; }
11
+ }
12
+ }
TaskTrackingSystem.Shared/Models/Dashboard/TaskStatusOverviewDto.cs ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using TaskTrackingSystem.Shared.Enums;
2
+ using AppTaskStatus = TaskTrackingSystem.Shared.Enums.AppTaskStatus;
3
+
4
+ namespace TaskTrackingSystem.Shared.Models.Dashboard
5
+ {
6
+ public class TaskStatusOverviewDto
7
+ {
8
+ public string StatusName { get; set; } = string.Empty;
9
+ public AppTaskStatus StatusId { get; set; }
10
+ public int TaskCount { get; set; }
11
+ }
12
+ }
TaskTrackingSystem.Shared/Models/Issue/CreateIssueDto.cs ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.ComponentModel.DataAnnotations;
4
+ using TaskTrackingSystem.Shared.Enums;
5
+ using TaskTrackingSystem.Shared.Localization;
6
+ using AppTaskStatus = TaskTrackingSystem.Shared.Enums.AppTaskStatus;
7
+
8
+ namespace TaskTrackingSystem.Shared.Models.Issue
9
+ {
10
+ public class CreateIssueDto : IValidatableObject
11
+ {
12
+ [Required]
13
+ [Range(1, long.MaxValue)]
14
+ public long TaskId { get; set; }
15
+
16
+ [MaxLength(200)]
17
+ public string Title { get; set; } = string.Empty;
18
+
19
+ [MaxLength(200)]
20
+ public string? TitleMy { get; set; }
21
+
22
+ [MaxLength(1000)]
23
+ public string? Description { get; set; }
24
+
25
+ [MaxLength(1000)]
26
+ public string? DescriptionMy { get; set; }
27
+
28
+ [Range(0, long.MaxValue)]
29
+ public long? AssignedTo { get; set; }
30
+
31
+ [Range(typeof(decimal), "0", "100000")]
32
+ public decimal? EstimatedHours { get; set; }
33
+
34
+ [Range(typeof(decimal), "0", "100000")]
35
+ public decimal? ActualHours { get; set; }
36
+
37
+ [MaxLength(300)]
38
+ public string? DelayReason { get; set; }
39
+
40
+ [MaxLength(300)]
41
+ public string? DelayReasonMy { get; set; }
42
+
43
+ public bool IsBlocked { get; set; }
44
+
45
+ [MaxLength(200)]
46
+ public string? BlockedBy { get; set; }
47
+
48
+ [MaxLength(200)]
49
+ public string? BlockedByMy { get; set; }
50
+
51
+ [Range(0, 3)]
52
+ public int EscalationLevel { get; set; }
53
+
54
+ [Required]
55
+ public DateTime StartDate { get; set; }
56
+
57
+ [Required]
58
+ public DateTime DueDate { get; set; }
59
+
60
+ [EnumDataType(typeof(AppTaskStatus))]
61
+ public AppTaskStatus StatusId { get; set; }
62
+
63
+ [EnumDataType(typeof(TaskPriority))]
64
+ public TaskPriority PriorityId { get; set; }
65
+
66
+ public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
67
+ {
68
+ if (string.IsNullOrWhiteSpace(Title) && string.IsNullOrWhiteSpace(TitleMy))
69
+ {
70
+ yield return new ValidationResult("At least one issue title is required.", new[] { nameof(Title), nameof(TitleMy) });
71
+ }
72
+
73
+ if (StartDate == default)
74
+ {
75
+ yield return new ValidationResult(AppLocalization.Text("validation.startDateRequired", "Start date is required."), new[] { nameof(StartDate) });
76
+ }
77
+
78
+ if (DueDate == default)
79
+ {
80
+ yield return new ValidationResult(AppLocalization.Text("validation.dueDateRequired", "Due date is required."), new[] { nameof(DueDate) });
81
+ }
82
+
83
+ if (StartDate != default && DueDate != default && DueDate.Date < StartDate.Date)
84
+ {
85
+ yield return new ValidationResult(AppLocalization.Text("validation.dueDateBeforeStart", "Due date cannot be earlier than start date."), new[] { nameof(DueDate), nameof(StartDate) });
86
+ }
87
+ }
88
+ }
89
+ }
TaskTrackingSystem.Shared/Models/Issue/IssueDto.cs ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using TaskTrackingSystem.Shared.Enums;
3
+
4
+ namespace TaskTrackingSystem.Shared.Models.Issue
5
+ {
6
+ public class IssueDto
7
+ {
8
+ public long Id { get; set; }
9
+ public long TaskId { get; set; }
10
+ public string TaskTitle { get; set; } = string.Empty;
11
+ public string? TaskTitleMy { get; set; }
12
+ public long ProjectId { get; set; }
13
+ public string ProjectName { get; set; } = string.Empty;
14
+ public string? ProjectNameMy { get; set; }
15
+ public string Title { get; set; } = string.Empty;
16
+ public string? TitleMy { get; set; }
17
+ public string? Description { get; set; }
18
+ public string? DescriptionMy { get; set; }
19
+ public long? AssignedTo { get; set; }
20
+ public string? AssignedToName { get; set; }
21
+ public string? AssignedToNameMy { get; set; }
22
+ public decimal? EstimatedHours { get; set; }
23
+ public decimal? ActualHours { get; set; }
24
+ public string? DelayReason { get; set; }
25
+ public string? DelayReasonMy { get; set; }
26
+ public bool IsBlocked { get; set; }
27
+ public string? BlockedBy { get; set; }
28
+ public string? BlockedByMy { get; set; }
29
+ public int EscalationLevel { get; set; }
30
+ public DateTime StartDate { get; set; }
31
+ public DateTime DueDate { get; set; }
32
+ public AppTaskStatus StatusId { get; set; }
33
+ public TaskPriority PriorityId { get; set; }
34
+ public DateTime CreatedAt { get; set; }
35
+ public DateTime? UpdatedAt { get; set; }
36
+ }
37
+ }
TaskTrackingSystem.Shared/Models/Issue/UpdateIssueDto.cs ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.ComponentModel.DataAnnotations;
4
+ using TaskTrackingSystem.Shared.Enums;
5
+ using TaskTrackingSystem.Shared.Localization;
6
+ using AppTaskStatus = TaskTrackingSystem.Shared.Enums.AppTaskStatus;
7
+
8
+ namespace TaskTrackingSystem.Shared.Models.Issue
9
+ {
10
+ public class UpdateIssueDto : IValidatableObject
11
+ {
12
+ [MaxLength(200)]
13
+ public string Title { get; set; } = string.Empty;
14
+
15
+ [MaxLength(200)]
16
+ public string? TitleMy { get; set; }
17
+
18
+ [MaxLength(1000)]
19
+ public string? Description { get; set; }
20
+
21
+ [MaxLength(1000)]
22
+ public string? DescriptionMy { get; set; }
23
+
24
+ [Range(0, long.MaxValue)]
25
+ public long? AssignedTo { get; set; }
26
+
27
+ [Range(typeof(decimal), "0", "100000")]
28
+ public decimal? EstimatedHours { get; set; }
29
+
30
+ [Range(typeof(decimal), "0", "100000")]
31
+ public decimal? ActualHours { get; set; }
32
+
33
+ [MaxLength(300)]
34
+ public string? DelayReason { get; set; }
35
+
36
+ [MaxLength(300)]
37
+ public string? DelayReasonMy { get; set; }
38
+
39
+ public bool IsBlocked { get; set; }
40
+
41
+ [MaxLength(200)]
42
+ public string? BlockedBy { get; set; }
43
+
44
+ [MaxLength(200)]
45
+ public string? BlockedByMy { get; set; }
46
+
47
+ [Range(0, 3)]
48
+ public int EscalationLevel { get; set; }
49
+
50
+ [Required]
51
+ public DateTime StartDate { get; set; }
52
+
53
+ [Required]
54
+ public DateTime DueDate { get; set; }
55
+
56
+ [EnumDataType(typeof(AppTaskStatus))]
57
+ public AppTaskStatus StatusId { get; set; }
58
+
59
+ [EnumDataType(typeof(TaskPriority))]
60
+ public TaskPriority PriorityId { get; set; }
61
+
62
+ public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
63
+ {
64
+ if (string.IsNullOrWhiteSpace(Title) && string.IsNullOrWhiteSpace(TitleMy))
65
+ {
66
+ yield return new ValidationResult("At least one issue title is required.", new[] { nameof(Title), nameof(TitleMy) });
67
+ }
68
+
69
+ if (StartDate == default)
70
+ {
71
+ yield return new ValidationResult(AppLocalization.Text("validation.startDateRequired", "Start date is required."), new[] { nameof(StartDate) });
72
+ }
73
+
74
+ if (DueDate == default)
75
+ {
76
+ yield return new ValidationResult(AppLocalization.Text("validation.dueDateRequired", "Due date is required."), new[] { nameof(DueDate) });
77
+ }
78
+
79
+ if (StartDate != default && DueDate != default && DueDate.Date < StartDate.Date)
80
+ {
81
+ yield return new ValidationResult(AppLocalization.Text("validation.dueDateBeforeStart", "Due date cannot be earlier than start date."), new[] { nameof(DueDate), nameof(StartDate) });
82
+ }
83
+ }
84
+ }
85
+ }
TaskTrackingSystem.Shared/Models/Menu/AccessMenuDto.cs ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ namespace TaskTrackingSystem.Shared.Models.Menu
2
+ {
3
+ public class AccessMenuDto
4
+ {
5
+ public string MenuCode { get; set; } = string.Empty;
6
+ public string ParentCode { get; set; } = string.Empty;
7
+ public string MenuName { get; set; } = string.Empty;
8
+ public string? MenuNameMy { get; set; }
9
+ public string? MenuUrl { get; set; }
10
+ public int OrderNo { get; set; }
11
+ public string? Icon { get; set; }
12
+ public bool Visible { get; set; }
13
+ public System.Collections.Generic.List<AccessPermissionDto> Permissions { get; set; } = new();
14
+ }
15
+ }
16
+
TaskTrackingSystem.Shared/Models/Menu/AccessPermissionDto.cs ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+
3
+ namespace TaskTrackingSystem.Shared.Models.Menu
4
+ {
5
+ public class AccessPermissionDto
6
+ {
7
+ public string PermissionId { get; set; } = string.Empty;
8
+ public string PermissionCode { get; set; } = string.Empty;
9
+ public string ParentMenuCode { get; set; } = string.Empty;
10
+ public string ActionName { get; set; } = string.Empty;
11
+ public string? ActionNameMy { get; set; }
12
+ public string ApiName { get; set; } = string.Empty;
13
+ public bool Visible { get; set; }
14
+ public int OrderNo { get; set; }
15
+ }
16
+ }
17
+
TaskTrackingSystem.Shared/Models/Menu/MenuDto.cs ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ namespace TaskTrackingSystem.Shared.Models.Menu
5
+ {
6
+ public class MenuDto
7
+ {
8
+ public string MenuCode { get; set; } = string.Empty;
9
+ public string ParentCode { get; set; } = string.Empty;
10
+ public string MenuName { get; set; } = string.Empty;
11
+ public string? MenuNameMy { get; set; }
12
+ public string? MenuUrl { get; set; }
13
+ public int OrderNo { get; set; }
14
+ public string? Icon { get; set; }
15
+ public List<MenuDto> SubMenus { get; set; } = new List<MenuDto>();
16
+ }
17
+ }
TaskTrackingSystem.Shared/Models/Notification/NotificationDto.cs ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+
3
+ namespace TaskTrackingSystem.Shared.Models.Notification;
4
+
5
+ public class NotificationDto
6
+ {
7
+ public long Id { get; set; }
8
+ public string Title { get; set; } = string.Empty;
9
+ public string? TitleMy { get; set; }
10
+ public string Body { get; set; } = string.Empty;
11
+ public string? BodyMy { get; set; }
12
+ public byte NotificationType { get; set; }
13
+ public string SourceType { get; set; } = string.Empty;
14
+ public long SourceId { get; set; }
15
+ public string TargetUrl { get; set; } = string.Empty;
16
+ public bool IsRead { get; set; }
17
+ public DateTime? CreatedAt { get; set; }
18
+ public DateTime? ReadAt { get; set; }
19
+ public string? SenderName { get; set; }
20
+ public string? SenderNameMy { get; set; }
21
+ }
TaskTrackingSystem.Shared/Models/Notification/NotificationNavigation.cs ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ namespace TaskTrackingSystem.Shared.Models.Notification;
2
+
3
+ public static class NotificationNavigation
4
+ {
5
+ public static string BuildTargetUrl(string sourceType, long sourceId, byte notificationType)
6
+ {
7
+ if (string.Equals(sourceType, "project", StringComparison.OrdinalIgnoreCase))
8
+ {
9
+ return $"/projects/{sourceId}/tasks";
10
+ }
11
+
12
+ if (string.Equals(sourceType, "task", StringComparison.OrdinalIgnoreCase))
13
+ {
14
+ var isCommentThread = notificationType is (byte)NotificationType.CommentAdded or (byte)NotificationType.Mention;
15
+ return isCommentThread
16
+ ? $"/tasks/{sourceId}/details?tab=comments"
17
+ : $"/tasks/{sourceId}/details";
18
+ }
19
+
20
+ return "/dashboard";
21
+ }
22
+ }
TaskTrackingSystem.Shared/Models/Notification/NotificationType.cs ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ namespace TaskTrackingSystem.Shared.Models.Notification;
2
+
3
+ public enum NotificationType : byte
4
+ {
5
+ TaskAssigned = 1,
6
+ StatusChanged = 2,
7
+ DueDateReminder = 3,
8
+ OverdueAlert = 4,
9
+ CommentAdded = 5,
10
+ Mention = 6,
11
+ PriorityChanged = 7,
12
+ ProjectUpdated = 8,
13
+ System = 9
14
+ }
TaskTrackingSystem.Shared/Models/Notification/RegisterDeviceTokenDto.cs ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ namespace TaskTrackingSystem.Shared.Models.Notification;
2
+
3
+ public class RegisterDeviceTokenDto
4
+ {
5
+ public string FcmToken { get; set; } = string.Empty;
6
+ }