├── .gitignore
├── Demo(2021-01-01).mp4
├── README.md
├── TimeTracker
└── TimeTracker
│ ├── .gitignore
│ ├── ClientApp
│ ├── .browserslistrc
│ ├── .env.development
│ ├── .env.production
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── Example.vue
│ ├── README.md
│ ├── babel.config.js
│ ├── dist
│ │ ├── css
│ │ │ ├── app.4b493da0.css
│ │ │ ├── app.4b493da0.css.map
│ │ │ ├── chunk-19e1a676.545f8971.css
│ │ │ ├── chunk-19e1a676.545f8971.css.map
│ │ │ ├── chunk-1b599696.0cd42b2a.css
│ │ │ ├── chunk-1b599696.0cd42b2a.css.map
│ │ │ ├── chunk-2d48622d.791d3332.css
│ │ │ ├── chunk-2d48622d.791d3332.css.map
│ │ │ ├── chunk-56927b24.a0d06f03.css
│ │ │ ├── chunk-56927b24.a0d06f03.css.map
│ │ │ ├── chunk-6568d9c2.e137935d.css
│ │ │ ├── chunk-6568d9c2.e137935d.css.map
│ │ │ ├── chunk-c7002a8e.b7228bf6.css
│ │ │ ├── chunk-c7002a8e.b7228bf6.css.map
│ │ │ ├── chunk-da9561ba.787b4019.css
│ │ │ ├── chunk-da9561ba.787b4019.css.map
│ │ │ ├── chunk-vendors.5605a41b.css
│ │ │ └── chunk-vendors.5605a41b.css.map
│ │ ├── favicon.ico
│ │ ├── fonts
│ │ │ ├── MaterialIcons-Regular.012cf6a1.woff
│ │ │ ├── MaterialIcons-Regular.0509ab09.0509ab09.woff2
│ │ │ ├── MaterialIcons-Regular.29b882f0.29b882f0.woff
│ │ │ ├── MaterialIcons-Regular.570eb838.woff2
│ │ │ ├── MaterialIcons-Regular.96c47680.96c47680.eot
│ │ │ ├── MaterialIcons-Regular.a37b0c01.ttf
│ │ │ ├── MaterialIcons-Regular.d120c85b.d120c85b.ttf
│ │ │ ├── MaterialIcons-Regular.e79bfd88.eot
│ │ │ ├── materialdesignicons-webfont.3d1f8fa2.eot
│ │ │ ├── materialdesignicons-webfont.3e722fd5.ttf
│ │ │ ├── materialdesignicons-webfont.4187121a.woff2
│ │ │ └── materialdesignicons-webfont.fec1b66a.woff
│ │ ├── img
│ │ │ ├── dark_wood.d35f4a25.png
│ │ │ ├── forest2.98aeac27.jpg
│ │ │ ├── grey_wash_wall.88ab8ad9.png
│ │ │ ├── icons
│ │ │ │ ├── android-chrome-192x192.png
│ │ │ │ ├── android-chrome-512x512.png
│ │ │ │ ├── android-chrome-maskable-192x192.png
│ │ │ │ ├── android-chrome-maskable-512x512.png
│ │ │ │ ├── apple-touch-icon-120x120.png
│ │ │ │ ├── apple-touch-icon-152x152.png
│ │ │ │ ├── apple-touch-icon-180x180.png
│ │ │ │ ├── apple-touch-icon-60x60.png
│ │ │ │ ├── apple-touch-icon-76x76.png
│ │ │ │ ├── apple-touch-icon.png
│ │ │ │ ├── favicon-16x16.png
│ │ │ │ ├── favicon-32x32.png
│ │ │ │ ├── msapplication-icon-144x144.png
│ │ │ │ ├── mstile-150x150.png
│ │ │ │ └── safari-pinned-tab.svg
│ │ │ ├── whitey.db698606.png
│ │ │ └── xv_light_blue.9693a7e2.png
│ │ ├── index.html
│ │ ├── js
│ │ │ ├── app.edd198db.js
│ │ │ ├── app.edd198db.js.map
│ │ │ ├── chunk-19e1a676.e3729111.js
│ │ │ ├── chunk-19e1a676.e3729111.js.map
│ │ │ ├── chunk-1b599696.103c08dd.js
│ │ │ ├── chunk-1b599696.103c08dd.js.map
│ │ │ ├── chunk-2d0c5b16.3a4e24dd.js
│ │ │ ├── chunk-2d0c5b16.3a4e24dd.js.map
│ │ │ ├── chunk-2d0c9224.ffd16665.js
│ │ │ ├── chunk-2d0c9224.ffd16665.js.map
│ │ │ ├── chunk-2d48622d.40df08ef.js
│ │ │ ├── chunk-2d48622d.40df08ef.js.map
│ │ │ ├── chunk-455734fe.fb5bdc63.js
│ │ │ ├── chunk-455734fe.fb5bdc63.js.map
│ │ │ ├── chunk-56927b24.e3053918.js
│ │ │ ├── chunk-56927b24.e3053918.js.map
│ │ │ ├── chunk-6568d9c2.a8fe224d.js
│ │ │ ├── chunk-6568d9c2.a8fe224d.js.map
│ │ │ ├── chunk-c7002a8e.ad326856.js
│ │ │ ├── chunk-c7002a8e.ad326856.js.map
│ │ │ ├── chunk-da9561ba.5704b6b6.js
│ │ │ ├── chunk-da9561ba.5704b6b6.js.map
│ │ │ ├── chunk-vendors.afce3364.js
│ │ │ └── chunk-vendors.afce3364.js.map
│ │ ├── manifest.json
│ │ ├── precache-manifest.2ccd831319ba6bd71793731fef02f545.js
│ │ ├── robots.txt
│ │ └── service-worker.js
│ ├── jest.config.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── img
│ │ │ └── icons
│ │ │ │ ├── android-chrome-192x192.png
│ │ │ │ ├── android-chrome-512x512.png
│ │ │ │ ├── android-chrome-maskable-192x192.png
│ │ │ │ ├── android-chrome-maskable-512x512.png
│ │ │ │ ├── apple-touch-icon-120x120.png
│ │ │ │ ├── apple-touch-icon-152x152.png
│ │ │ │ ├── apple-touch-icon-180x180.png
│ │ │ │ ├── apple-touch-icon-60x60.png
│ │ │ │ ├── apple-touch-icon-76x76.png
│ │ │ │ ├── apple-touch-icon.png
│ │ │ │ ├── favicon-16x16.png
│ │ │ │ ├── favicon-32x32.png
│ │ │ │ ├── msapplication-icon-144x144.png
│ │ │ │ ├── mstile-150x150.png
│ │ │ │ └── safari-pinned-tab.svg
│ │ ├── index.html
│ │ └── robots.txt
│ ├── src
│ │ ├── App.vue
│ │ ├── api
│ │ │ ├── admin.ts
│ │ │ ├── authentication.ts
│ │ │ ├── imges.ts
│ │ │ ├── parameter.ts
│ │ │ ├── period.ts
│ │ │ ├── taskEditor.ts
│ │ │ ├── taskReporter.ts
│ │ │ └── webSocket.ts
│ │ ├── assets
│ │ │ ├── img
│ │ │ │ ├── always_grey.png
│ │ │ │ ├── blue_wash_wall_1.png
│ │ │ │ ├── blue_wash_wall_2.png
│ │ │ │ ├── blue_wash_wall_3.png
│ │ │ │ ├── blue_wash_wall_4.png
│ │ │ │ ├── crossword.png
│ │ │ │ ├── dark_wood.png
│ │ │ │ ├── dust_scratches.png
│ │ │ │ ├── fabric_of_squares_gray.png
│ │ │ │ ├── footer_lodyas.png
│ │ │ │ ├── footer_lodyas2.png
│ │ │ │ ├── forest
│ │ │ │ │ ├── forest1.jpg
│ │ │ │ │ └── forest2.jpg
│ │ │ │ ├── grey_wash_wall.png
│ │ │ │ ├── noisy_grid.png
│ │ │ │ ├── noisy_grid2.png
│ │ │ │ ├── pikachou.gif
│ │ │ │ ├── project_papper.png
│ │ │ │ ├── triangular.png
│ │ │ │ ├── vichy-dark.png
│ │ │ │ ├── vichy.png
│ │ │ │ ├── whitey copy.png
│ │ │ │ ├── whitey-light.png
│ │ │ │ ├── whitey.png
│ │ │ │ ├── xv.png
│ │ │ │ └── xv_light_blue.png
│ │ │ └── logo.png
│ │ ├── components
│ │ │ ├── GitHubCorner.vue
│ │ │ ├── PageFlipper.vue
│ │ │ ├── auth
│ │ │ │ ├── AdminAccounts.vue
│ │ │ │ ├── AdminUncheckAccounts.vue
│ │ │ │ ├── PageIdle.vue
│ │ │ │ ├── PageIdleModalSimple.vue
│ │ │ │ ├── Register.vue
│ │ │ │ ├── Settings.vue
│ │ │ │ ├── SettingsImageEditor.vue
│ │ │ │ ├── SignIn.vue
│ │ │ │ └── SignInModal.vue
│ │ │ ├── card
│ │ │ │ └── GeneralCard.vue
│ │ │ ├── clock
│ │ │ │ ├── AnalogClock.vue
│ │ │ │ ├── Clock.vue
│ │ │ │ └── DigitalClock.vue
│ │ │ ├── img
│ │ │ │ ├── SimpleImageEditor.vue
│ │ │ │ └── SimpleImageEditorModal.vue
│ │ │ ├── nav
│ │ │ │ ├── Nav.vue
│ │ │ │ ├── NavGithubLink.vue
│ │ │ │ ├── NavIndividualSettings.vue
│ │ │ │ ├── NavItem.vue
│ │ │ │ ├── NavLinkItem.vue
│ │ │ │ ├── NavProgress.vue
│ │ │ │ └── TwoColumnSidebarToggleIcon.vue
│ │ │ └── trackTask
│ │ │ │ ├── charts
│ │ │ │ ├── EchartsPie.vue
│ │ │ │ ├── PieRowData.vue
│ │ │ │ ├── TaskPeriodEchartsLine.vue
│ │ │ │ ├── TaskPeriodSimpleSummary.vue
│ │ │ │ ├── greenTheme.json
│ │ │ │ └── taskPeriodSimpleSummary.ts
│ │ │ │ ├── taskForm
│ │ │ │ ├── TaskDayForm.vue
│ │ │ │ ├── TaskDayLeaveCheckbox.vue
│ │ │ │ ├── TaskDayLeaveIcon.vue
│ │ │ │ ├── TaskDayTimeline.vue
│ │ │ │ ├── TaskDayTimelineTitle.vue
│ │ │ │ └── taskform.ts
│ │ │ │ ├── toolbar
│ │ │ │ ├── DateMenuSelector.vue
│ │ │ │ ├── DatePeriodSelector.vue
│ │ │ │ ├── DatePicker.vue
│ │ │ │ ├── DateRangeSelector.vue
│ │ │ │ ├── MetaDisplayer.vue
│ │ │ │ └── TargetModalSelector.vue
│ │ │ │ └── trackSettings
│ │ │ │ ├── DatePeriodForm.vue
│ │ │ │ ├── DatePeriodFormAdd.vue
│ │ │ │ ├── DatePeriodFormUpdate.vue
│ │ │ │ ├── OptionForm.vue
│ │ │ │ ├── OptionFormAdd.vue
│ │ │ │ └── OptionFormUpdate.vue
│ │ ├── configGtag.ts
│ │ ├── jctk-table-form.d.ts
│ │ ├── main.ts
│ │ ├── models
│ │ │ ├── authentication.ts
│ │ │ ├── charts.ts
│ │ │ ├── constants
│ │ │ │ ├── authentication.ts
│ │ │ │ ├── task.ts
│ │ │ │ └── webSocket.ts
│ │ │ ├── nav.ts
│ │ │ ├── notification.ts
│ │ │ ├── pageIdle.ts
│ │ │ ├── parameter.ts
│ │ │ ├── period.ts
│ │ │ ├── routeConfigs.ts
│ │ │ ├── store.ts
│ │ │ ├── tasks.ts
│ │ │ ├── twoColumn.ts
│ │ │ └── webSocket.ts
│ │ ├── registerServiceWorker.ts
│ │ ├── router
│ │ │ ├── index.ts
│ │ │ ├── routeConfigs.ts
│ │ │ └── routeRoleValidation.ts
│ │ ├── scss
│ │ │ ├── example.scss
│ │ │ ├── global_var.scss
│ │ │ └── var.scss
│ │ ├── shims-tsx.d.ts
│ │ ├── shims-vue.d.ts
│ │ ├── store
│ │ │ ├── authentication.ts
│ │ │ ├── index.ts
│ │ │ ├── notification.ts
│ │ │ ├── pageIdle.ts
│ │ │ ├── taskParameters.ts
│ │ │ ├── twoColumn.ts
│ │ │ └── webSocket.ts
│ │ ├── util
│ │ │ ├── apiHandler.ts
│ │ │ ├── authValidation.ts
│ │ │ ├── authentication.ts
│ │ │ ├── breakPoint.ts
│ │ │ ├── components
│ │ │ │ ├── layout
│ │ │ │ │ └── ResponsiveElement.vue
│ │ │ │ └── transition
│ │ │ │ │ ├── RippleTransition.vue
│ │ │ │ │ ├── RippleTransitionFlip.vue
│ │ │ │ │ └── SimpleTransition.vue
│ │ │ ├── notification.ts
│ │ │ ├── period.ts
│ │ │ ├── taskDate.ts
│ │ │ └── taskParameters.ts
│ │ ├── views
│ │ │ ├── Home.vue
│ │ │ ├── IndividualSettings.vue
│ │ │ ├── PermissionEditor.vue
│ │ │ ├── Registration.vue
│ │ │ ├── TaskEditor.vue
│ │ │ ├── TaskReporter.vue
│ │ │ ├── TaskSummaryReporter.vue
│ │ │ ├── TrackSettings.vue
│ │ │ └── layouts
│ │ │ │ ├── Center.vue
│ │ │ │ ├── Container.vue
│ │ │ │ ├── Tab.vue
│ │ │ │ └── TwoColumn.vue
│ │ ├── vue-echarts.d.ts
│ │ ├── vuetify-image-input.d.ts
│ │ └── vuetify.ts
│ ├── tests
│ │ └── unit
│ │ │ └── example.spec.ts
│ ├── tsconfig.json
│ ├── vue.config.js
│ └── webpack.config.js
│ ├── Controllers
│ ├── AccountController.cs
│ ├── AdminController.cs
│ ├── ImageController.cs
│ ├── ParameterController.cs
│ ├── PeriodController.cs
│ ├── TaskEditorController.cs
│ └── TaskReporterController.cs
│ ├── DAL
│ ├── ApplicationDbContext.cs
│ ├── Attributes
│ │ ├── SqlDefaultValueAttribute.cs
│ │ └── UniqueAttribute.cs
│ ├── DBModels
│ │ ├── Auth
│ │ │ ├── MapUserRole.cs
│ │ │ ├── User.cs
│ │ │ ├── UserImage.cs
│ │ │ └── UserRole.cs
│ │ ├── Task
│ │ │ ├── DayWorkLimitTime.cs
│ │ │ ├── NonWorkDays.cs
│ │ │ ├── Period.cs
│ │ │ ├── Task.cs
│ │ │ ├── TaskDay.cs
│ │ │ ├── TaskSource.cs
│ │ │ ├── TaskTimeRange.cs
│ │ │ └── TaskType.cs
│ │ └── UserRole.cs
│ ├── DBModelsBase
│ │ ├── EntityDateInfo.cs
│ │ ├── EntityOptions.cs
│ │ ├── EntityTask.cs
│ │ └── EntityTaskBase.cs
│ ├── IUserAuthHandler.cs
│ ├── Models
│ │ ├── AccountStatus.cs
│ │ ├── CreateAccountResult.cs
│ │ └── UserRoles.cs
│ ├── ParameterHandler.cs
│ ├── SeedData.cs
│ ├── SeedDataImg.cs
│ ├── TaskHandler.cs
│ ├── TaskReportHandler.cs
│ └── UserAuthHandler.cs
│ ├── Helper
│ ├── Attributes
│ │ ├── DateFormatConverter.cs
│ │ ├── GuidNotEmptyAttribute.cs
│ │ └── ListNotEmptyAttribute.cs
│ ├── Auth
│ │ ├── AuthorizeRoleAttribute.cs
│ │ └── SecurePasswordHasher.cs
│ └── Extensions
│ │ ├── DBContextExtension.cs
│ │ ├── DateTimeExtension.cs
│ │ ├── SessionAuthExtensions.cs
│ │ ├── SessionExtensions.cs
│ │ └── SqlServiceCollectionExtensions.cs
│ ├── Hubs
│ ├── BaseWSHub.cs
│ ├── Models
│ │ └── WSMapCode.cs
│ ├── UserIdProvider.cs
│ ├── WSHub.cs
│ └── WSHubHandler.cs
│ ├── MappingProfile.cs
│ ├── Migrations
│ ├── 20201226131516_InitialCreate.Designer.cs
│ ├── 20201226131516_InitialCreate.cs
│ └── ApplicationDbContextModelSnapshot.cs
│ ├── Models
│ ├── ApprovedUser.cs
│ ├── AuthenticationUser.cs
│ ├── Period
│ │ └── QueryPeriod.cs
│ ├── RegistUser.cs
│ ├── SessionUserData.cs
│ ├── Task
│ │ ├── CreateTask.cs
│ │ ├── DeleteTasks.cs
│ │ ├── GetDaysDataResponse.cs
│ │ ├── MultiSimpleSummary.cs
│ │ ├── PersonSummary.cs
│ │ ├── PieRow.cs
│ │ ├── QueryPeopleTasks.cs
│ │ ├── QueryTasks.cs
│ │ ├── SimpleSummary.cs
│ │ ├── UpdateIsLeave.cs
│ │ ├── UpdateTaskCol.cs
│ │ └── UpdateTaskRowOrder.cs
│ ├── UpdateAccount.cs
│ ├── UpdatePassword.cs
│ ├── UpdateUser.cs
│ └── UserInfo.cs
│ ├── Pages
│ ├── Error.cshtml
│ ├── Error.cshtml.cs
│ └── _ViewImports.cshtml
│ ├── Program.cs
│ ├── Properties
│ └── launchSettings.json
│ ├── Startup.cs
│ ├── TimeTracker.csproj
│ ├── appsettings.Development.json
│ └── appsettings.json
├── release_windows
├── CreateDB_CreateTable_CreateInitData.sql
└── publish.zip
└── screen_shot.png
/Demo(2021-01-01).mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/Demo(2021-01-01).mp4
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/.env.development:
--------------------------------------------------------------------------------
1 | VUE_APP_SERVER_URL=https://localhost:44328/
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/.env.production:
--------------------------------------------------------------------------------
1 | VUE_APP_SERVER_URL=/
2 | VUE_APP_GA_ID=UA-138418210-2
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | 'extends': [
7 | 'plugin:vue/essential',
8 | 'eslint:recommended',
9 | '@vue/typescript/recommended'
10 | ],
11 | parserOptions: {
12 | ecmaVersion: 2020
13 | },
14 | rules: {
15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
17 | "@typescript-eslint/interface-name-prefix": 'off',
18 | },
19 | overrides: [
20 | {
21 | files: [
22 | '**/__tests__/*.{j,t}s?(x)',
23 | '**/tests/unit/**/*.spec.{j,t}s?(x)'
24 | ],
25 | env: {
26 | jest: true
27 | }
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/Example.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
23 |
24 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/README.md:
--------------------------------------------------------------------------------
1 | # test-composition
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Run your unit tests
19 | ```
20 | npm run test:unit
21 | ```
22 |
23 | ### Lints and fixes files
24 | ```
25 | npm run lint
26 | ```
27 |
28 | ### Customize configuration
29 | See [Configuration Reference](https://cli.vuejs.org/config/).
30 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/css/chunk-6568d9c2.e137935d.css:
--------------------------------------------------------------------------------
1 | hr.vm-style-1[data-v-55194cea]{border:0;height:1px;background-image:linear-gradient(90deg,transparent,rgba(0,0,0,.75),transparent)}hr.vm-style-1-2[data-v-55194cea]{border:0;height:1px;background-image:linear-gradient(90deg,transparent,#496586,transparent)}hr.vm-style-2[data-v-55194cea]{height:10px;border:1;box-shadow:inset 0 9px 9px -3px rgba(0,0,0,.5);border-radius:5px}hr.vm-style-3[data-v-55194cea]{border:0;height:0;box-shadow:0 0 10px 1px #000}hr.vm-style-3[data-v-55194cea]:after{content:"\00a0"}.text-shadow-1[data-v-55194cea]{text-shadow:2px 2px 2px #aaa}.text-shadow-2[data-v-55194cea]{text-shadow:1px 1px 0 #fff,2px 2px 0 #000}.drop-shadow-1[data-v-55194cea]{filter:drop-shadow(12px 12px 7px rgba(0,0,0,.701961))}.drop-shadow-2[data-v-55194cea]{filter:drop-shadow(5px 5px 6px rgba(0,0,0,.701961))}i.material-icons[data-v-55194cea]::-moz-selection{color:initial}i.material-icons[data-v-55194cea]::selection{color:initial}[data-v-55194cea]{box-sizing:border-box}body[data-v-55194cea]{display:block;margin:0}hr.vm-test[data-v-55194cea]{margin:0 25px;height:500px;width:1px;background:#000;background:-webkit-gradient(linear,180 180,100% 0,from(#fff),to(#fff),color-stop(50%,#000))}.flex-center[data-v-55194cea]{display:flex;justify-content:center}.margin-center[data-v-55194cea]{margin:0 auto}.theme-table.theme--light.v-data-table tbody tr.v-data-table__selected[data-v-55194cea]{background:#607d8b;color:#d3d3d3}.theme-table.theme--light.v-data-table tbody tr.v-data-table__selected[data-v-55194cea]:hover{background:#009688!important;color:#d3d3d3!important}html[data-v-55194cea]{overflow:auto;overflow-y:auto!important}html[data-v-55194cea]::-webkit-scrollbar{width:14px;height:12px;background-color:#eaeaea}html[data-v-55194cea]::-webkit-scrollbar-thumb{border-radius:10px;-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,.3);background-color:#d8d8d8}html[data-v-55194cea]::-webkit-scrollbar-thumb:hover{background-color:#ecebeb}.animate__animated.animate__fastest[data-v-55194cea]{-webkit-animation-duration:.25s;animation-duration:.25s}.user-image-nav[data-v-55194cea]{height:48px;width:48px;border-radius:48px}.user-image-big[data-v-55194cea]{height:256px;width:256px;border-radius:256px}.TaskSummaryReporter[data-v-55194cea]{width:100%}
2 | /*# sourceMappingURL=chunk-6568d9c2.e137935d.css.map */
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/favicon.ico
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.012cf6a1.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.012cf6a1.woff
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.0509ab09.0509ab09.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.0509ab09.0509ab09.woff2
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.29b882f0.29b882f0.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.29b882f0.29b882f0.woff
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.570eb838.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.570eb838.woff2
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.96c47680.96c47680.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.96c47680.96c47680.eot
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.a37b0c01.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.a37b0c01.ttf
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.d120c85b.d120c85b.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.d120c85b.d120c85b.ttf
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.e79bfd88.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/MaterialIcons-Regular.e79bfd88.eot
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/materialdesignicons-webfont.3d1f8fa2.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/materialdesignicons-webfont.3d1f8fa2.eot
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/materialdesignicons-webfont.3e722fd5.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/materialdesignicons-webfont.3e722fd5.ttf
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/materialdesignicons-webfont.4187121a.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/materialdesignicons-webfont.4187121a.woff2
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/fonts/materialdesignicons-webfont.fec1b66a.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/fonts/materialdesignicons-webfont.fec1b66a.woff
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/dark_wood.d35f4a25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/dark_wood.d35f4a25.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/forest2.98aeac27.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/forest2.98aeac27.jpg
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/grey_wash_wall.88ab8ad9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/grey_wash_wall.88ab8ad9.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/android-chrome-maskable-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/android-chrome-maskable-192x192.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/android-chrome-maskable-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/android-chrome-maskable-512x512.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/apple-touch-icon.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/msapplication-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/msapplication-icon-144x144.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/icons/mstile-150x150.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/icons/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/whitey.db698606.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/whitey.db698606.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/img/xv_light_blue.9693a7e2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/dist/img/xv_light_blue.9693a7e2.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/index.html:
--------------------------------------------------------------------------------
1 |
TimeTracker We're sorry but TimeTracker doesn't work properly without JavaScript enabled. Please enable it to continue.
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/js/chunk-2d0c5b16.3a4e24dd.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0c5b16"],{"3fd1":function(e,n,t){"use strict";t.r(n);var a=function(){var e=this,n=e.$createElement,t=e._self._c||n;return t("Container",[t("Center",{staticClass:"Registration"},[t("Register")],1)],1)},r=[],s=t("a6f4"),i=t("3d0f"),o=t("9fb8"),c=t("f102"),f=Object(s["defineComponent"])({name:"Registration",props:{},components:{Container:i["a"],Center:o["a"],Register:c["a"]}}),p=f,u=t("2877"),d=Object(u["a"])(p,a,r,!1,null,"d7e79828",null);n["default"]=d.exports}}]);
2 | //# sourceMappingURL=chunk-2d0c5b16.3a4e24dd.js.map
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/js/chunk-2d0c5b16.3a4e24dd.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["webpack:///./src/views/Registration.vue?48b3","webpack:///./src/views/Registration.vue","webpack:///./src/views/Registration.vue?f379","webpack:///./src/views/Registration.vue?3428"],"names":["render","_vm","this","_h","$createElement","_c","_self","staticClass","staticRenderFns","name","props","components","Container","Center","Register","component"],"mappings":"yHAAA,IAAIA,EAAS,WAAa,IAAIC,EAAIC,KAASC,EAAGF,EAAIG,eAAmBC,EAAGJ,EAAIK,MAAMD,IAAIF,EAAG,OAAOE,EAAG,YAAY,CAACA,EAAG,SAAS,CAACE,YAAY,gBAAgB,CAACF,EAAG,aAAa,IAAI,IAC1KG,EAAkB,G,gDCcP,+BAAgB,CAC3BC,KAAM,eACNC,MAAM,GAGNC,WAAW,CACPC,YAAA,KACAC,SAAA,KACAC,WAAA,QCvB+X,I,YCOnYC,EAAY,eACd,EACAf,EACAQ,GACA,EACA,KACA,WACA,MAIa,aAAAO,E","file":"js/chunk-2d0c5b16.3a4e24dd.js","sourcesContent":["var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Container',[_c('Center',{staticClass:\"Registration\"},[_c('Register')],1)],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n\n\n\nimport { defineComponent, ref } from '@vue/composition-api'\n\nimport Container from './layouts/Container.vue'\nimport Center from './layouts/Center.vue'\nimport Register from '@/components/auth/Register.vue'\n\nexport default defineComponent({\n name: 'Registration',\n props:{\n\n },\n components:{\n Container,\n Center,\n Register\n }, \n})\n","import mod from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--14-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/ts-loader/index.js??ref--14-3!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Registration.vue?vue&type=script&lang=ts&\"; export default mod; export * from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--14-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/ts-loader/index.js??ref--14-3!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Registration.vue?vue&type=script&lang=ts&\"","import { render, staticRenderFns } from \"./Registration.vue?vue&type=template&id=d7e79828&scoped=true&\"\nimport script from \"./Registration.vue?vue&type=script&lang=ts&\"\nexport * from \"./Registration.vue?vue&type=script&lang=ts&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"d7e79828\",\n null\n \n)\n\nexport default component.exports"],"sourceRoot":""}
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/js/chunk-6568d9c2.a8fe224d.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-6568d9c2"],{"153c":function(e,t,n){"use strict";n.r(t);var a=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("Container",[n("div",{staticClass:"TaskSummaryReporter"},[n("TaskReporter",{attrs:{multipleMode:""}})],1)])},r=[],o=n("a6f4"),s=n("3d0f"),c=n("9944"),u=Object(o["defineComponent"])({name:"TaskSummaryReporter",props:{},components:{Container:s["a"],TaskReporter:c["default"]}}),i=u,p=(n("eb85"),n("2877")),l=Object(p["a"])(i,a,r,!1,null,"55194cea",null);t["default"]=l.exports},be0e:function(e,t,n){},eb85:function(e,t,n){"use strict";n("be0e")}}]);
2 | //# sourceMappingURL=chunk-6568d9c2.a8fe224d.js.map
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/manifest.json:
--------------------------------------------------------------------------------
1 | {"name":"TimeTracker","short_name":"TimeTracker","theme_color":"#4DBA87","icons":[{"src":"./img/icons/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"./img/icons/android-chrome-512x512.png","sizes":"512x512","type":"image/png"},{"src":"./img/icons/android-chrome-maskable-192x192.png","sizes":"192x192","type":"image/png","purpose":"maskable"},{"src":"./img/icons/android-chrome-maskable-512x512.png","sizes":"512x512","type":"image/png","purpose":"maskable"}],"start_url":".","display":"standalone","background_color":"#000000"}
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/dist/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Welcome to your Workbox-powered service worker!
3 | *
4 | * You'll need to register this file in your web app and you should
5 | * disable HTTP caching for this file too.
6 | * See https://goo.gl/nhQhGp
7 | *
8 | * The rest of the code is auto-generated. Please don't update this file
9 | * directly; instead, make changes to your Workbox build configuration
10 | * and re-run your build process.
11 | * See https://goo.gl/2aRDsh
12 | */
13 |
14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15 |
16 | importScripts(
17 | "/precache-manifest.2ccd831319ba6bd71793731fef02f545.js"
18 | );
19 |
20 | workbox.core.setCacheNameDetails({prefix: "TimeTracker"});
21 |
22 | self.addEventListener('message', (event) => {
23 | if (event.data && event.data.type === 'SKIP_WAITING') {
24 | self.skipWaiting();
25 | }
26 | });
27 |
28 | /**
29 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to
30 | * requests for URLs in the manifest.
31 | * See https://goo.gl/S9QRab
32 | */
33 | self.__precacheManifest = [].concat(self.__precacheManifest || []);
34 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
35 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel'
3 | }
4 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TimeTracker",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "test:unit": "vue-cli-service test:unit",
9 | "lint": "vue-cli-service lint"
10 | },
11 | "dependencies": {
12 | "@vue/composition-api": "^1.0.0-beta.20",
13 | "ci": "^1.0.0",
14 | "core-js": "^3.6.5",
15 | "register-service-worker": "^1.7.1",
16 | "vue": "^2.6.11",
17 | "vue-router": "^3.2.0",
18 | "vuex": "^3.4.0"
19 | },
20 | "devDependencies": {
21 | "@mdi/font": "^5.8.55",
22 | "@microsoft/signalr": "^5.0.0",
23 | "@types/echarts": "^4.9.3",
24 | "@types/jest": "^24.0.19",
25 | "@types/jquery": "^3.5.4",
26 | "@types/lodash.debounce": "^4.0.6",
27 | "@types/lodash.throttle": "^4.1.6",
28 | "@typescript-eslint/eslint-plugin": "^2.33.0",
29 | "@typescript-eslint/parser": "^2.33.0",
30 | "@vue/cli-plugin-babel": "~4.5.0",
31 | "@vue/cli-plugin-eslint": "~4.5.0",
32 | "@vue/cli-plugin-pwa": "~4.5.0",
33 | "@vue/cli-plugin-router": "~4.5.0",
34 | "@vue/cli-plugin-typescript": "~4.5.0",
35 | "@vue/cli-plugin-unit-jest": "~4.5.0",
36 | "@vue/cli-plugin-vuex": "~4.5.0",
37 | "@vue/cli-service": "~4.5.0",
38 | "@vue/eslint-config-typescript": "^5.0.2",
39 | "@vue/test-utils": "^1.0.3",
40 | "animate.css": "^4.1.1",
41 | "axios": "^0.21.0",
42 | "echarts": "^4.9.0",
43 | "eslint": "^6.7.2",
44 | "eslint-plugin-vue": "^6.2.2",
45 | "jctk-table-form": "^0.1.11",
46 | "jquery": "^3.5.1",
47 | "lodash.debounce": "^4.0.8",
48 | "lodash.throttle": "^4.1.1",
49 | "material-design-icons": "^3.0.1",
50 | "moment": "^2.29.1",
51 | "node-sass": "^4.12.0",
52 | "sass-loader": "^8.0.2",
53 | "typescript": "~3.9.3",
54 | "velocity-animate": "^1.5.2",
55 | "vue-analog-clock": "^1.0.2",
56 | "vue-clickaway2": "^2.3.2",
57 | "vue-echarts": "^4.1.0",
58 | "vue-gtag": "^1.10.0",
59 | "vue-template-compiler": "^2.6.11",
60 | "vue-toastification": "^1.7.8",
61 | "vue-use-web": "^1.0.1",
62 | "vuetify": "^2.3.19",
63 | "vuetify-image-input": "^19.1.0",
64 | "vuex-multi-tab-state": "^1.0.15"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/favicon.ico
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/android-chrome-maskable-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/android-chrome-maskable-192x192.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/android-chrome-maskable-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/android-chrome-maskable-512x512.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/apple-touch-icon.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/msapplication-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/msapplication-icon-144x144.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/public/img/icons/mstile-150x150.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/img/icons/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
12 | We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/api/admin.ts:
--------------------------------------------------------------------------------
1 | import { AxiosResponse } from 'axios'
2 | import { Ref } from '@vue/composition-api'
3 |
4 | import { APIHandler } from '@/util/apiHandler.ts'
5 | import { Store } from 'vuex/types/index'
6 | import VueRouter from 'vue-router'
7 | import { IStore } from '@/models/store'
8 | import { IUpdateAccounts } from '@/models/authentication'
9 |
10 | class AdminAPIHandler extends APIHandler{
11 |
12 | constructor( store: Store, router: VueRouter ) {
13 | super(store, router)
14 | }
15 |
16 | GetUncheckAccounts(isLoading: Ref,
17 | SuccessFunc?: (response: AxiosResponse) => void,
18 | ErrorFunc?: (error: any) => void){
19 | this.HttpPost('api/Admin/GetUncheckAccounts', isLoading, undefined, SuccessFunc, ErrorFunc)
20 | }
21 |
22 | GetAccounts(isLoading: Ref,
23 | SuccessFunc?: (response: AxiosResponse) => void,
24 | ErrorFunc?: (error: any) => void){
25 | this.HttpPost('api/Admin/GetAccounts', isLoading, undefined, SuccessFunc, ErrorFunc)
26 | }
27 |
28 | UpdateAccounts(data: Array, isLoading: Ref,
29 | SuccessFunc?: (response: AxiosResponse) => void,
30 | ErrorFunc?: (error: any) => void){
31 | this.HttpPost('api/Admin/UpdateAccounts', isLoading, data, SuccessFunc, ErrorFunc)
32 | }
33 | }
34 |
35 | export {
36 | AdminAPIHandler
37 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/api/imges.ts:
--------------------------------------------------------------------------------
1 | import { IStore } from "@/models/store"
2 | import { APIHandler } from "@/util/apiHandler"
3 | import { Ref } from "@vue/composition-api"
4 | import { AxiosResponse } from "axios"
5 | import VueRouter from "vue-router"
6 | import { Store } from "vuex/types/index"
7 |
8 |
9 | class ImageAPIHandler extends APIHandler{
10 |
11 | constructor( store: Store, router: VueRouter ) {
12 | super(store, router)
13 | }
14 |
15 | GetImage(isLoading?: Ref,
16 | SuccessFunc?: (response: AxiosResponse) => void,
17 | ErrorFunc?: (error: any) => void){
18 | this.HttpPost('api/Image/GetImage', isLoading, undefined, (response) => {
19 | this._store.commit("SetUserImage", response.data)
20 | if (SuccessFunc) {
21 | SuccessFunc(response)
22 | }
23 | }, ErrorFunc)
24 | }
25 |
26 | CreateOrUpdateImage(data: string, isLoading: Ref,
27 | SuccessFunc?: (response: AxiosResponse) => void,
28 | ErrorFunc?: (error: any) => void){
29 | this.HttpPost('api/Image/CreateOrUpdateImage', isLoading, { image: data }, (response) => {
30 | this._store.commit("SetUserImage", data)
31 | if (SuccessFunc) {
32 | SuccessFunc(response)
33 | }
34 | }, ErrorFunc)
35 | }
36 |
37 | DeleteImage(isLoading: Ref,
38 | SuccessFunc?: (response: AxiosResponse) => void,
39 | ErrorFunc?: (error: any) => void){
40 | this.HttpPost('api/Image/DeleteImage', isLoading, undefined, (response) => {
41 | this._store.commit("SetUserImage", "")
42 | if (SuccessFunc) {
43 | SuccessFunc(response)
44 | }
45 | }, ErrorFunc)
46 | }
47 | }
48 |
49 | export {
50 | ImageAPIHandler
51 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/api/period.ts:
--------------------------------------------------------------------------------
1 | import { IPeriod } from "@/models/period"
2 | import { IStore } from "@/models/store"
3 | import { APIHandler } from "@/util/apiHandler"
4 | import { Ref } from "@vue/composition-api"
5 | import { AxiosResponse } from "axios"
6 | import VueRouter from "vue-router"
7 | import { Store } from "vuex/types/index"
8 |
9 |
10 | class PeriodAPIHandler extends APIHandler{
11 |
12 | constructor( store: Store, router: VueRouter ) {
13 | super(store, router)
14 | }
15 |
16 | GetPeriods(isLoading: Ref,
17 | SuccessFunc?: (response: AxiosResponse) => void,
18 | ErrorFunc?: (error: any) => void){
19 | this.HttpPost('api/Period/GetPeriods', isLoading, undefined, SuccessFunc, ErrorFunc)
20 | }
21 |
22 | CreatePeriods(data: Array, isLoading: Ref,
23 | SuccessFunc?: (response: AxiosResponse) => void,
24 | ErrorFunc?: (error: any) => void){
25 | this.HttpPost('api/Period/CreatePeriods', isLoading, data, SuccessFunc, ErrorFunc)
26 | }
27 |
28 | UpdatePeriods(data: Array, isLoading: Ref,
29 | SuccessFunc?: (response: AxiosResponse) => void,
30 | ErrorFunc?: (error: any) => void){
31 | this.HttpPost('api/Period/UpdatePeriods', isLoading, data, SuccessFunc, ErrorFunc)
32 | }
33 |
34 | DeletePeriods(data: Array, isLoading: Ref,
35 | SuccessFunc?: (response: AxiosResponse) => void,
36 | ErrorFunc?: (error: any) => void){
37 | this.HttpPost('api/Period/DeletePeriods', isLoading, data, SuccessFunc, ErrorFunc)
38 | }
39 | }
40 |
41 | export {
42 | PeriodAPIHandler
43 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/api/taskReporter.ts:
--------------------------------------------------------------------------------
1 | import { IStore } from "@/models/store"
2 | import { IQueryPeopleTasks } from "@/models/tasks"
3 | import { APIHandler } from "@/util/apiHandler"
4 | import { Ref } from "@vue/composition-api"
5 | import { AxiosResponse } from "axios"
6 | import VueRouter from "vue-router"
7 | import { Store } from "vuex/types/index"
8 |
9 |
10 | class TaskReporterAPIHandler extends APIHandler{
11 |
12 | constructor( store: Store, router: VueRouter ) {
13 | super(store, router)
14 | }
15 |
16 | GetSimpleSummary(data: IQueryPeopleTasks, isLoading: Ref,
17 | SuccessFunc?: (response: AxiosResponse) => void,
18 | ErrorFunc?: (error: any) => void){
19 | this.HttpPost('api/TaskReporter/GetSimpleSummary', isLoading, data, SuccessFunc, ErrorFunc)
20 | }
21 |
22 | GetTaskTimeSummaryDetail(data: IQueryPeopleTasks, isLoading: Ref,
23 | SuccessFunc?: (response: AxiosResponse) => void,
24 | ErrorFunc?: (error: any) => void){
25 | this.HttpPost('api/TaskReporter/GetTaskTimeSummaryDetail', isLoading, data, SuccessFunc, ErrorFunc)
26 | }
27 |
28 | GetTaskTypeSummary(data: IQueryPeopleTasks, isLoading: Ref,
29 | SuccessFunc?: (response: AxiosResponse) => void,
30 | ErrorFunc?: (error: any) => void){
31 | this.HttpPost('api/TaskReporter/GetTaskTypeSummary', isLoading, data, SuccessFunc, ErrorFunc)
32 | }
33 |
34 | GetTaskSourceSummary(data: IQueryPeopleTasks, isLoading: Ref,
35 | SuccessFunc?: (response: AxiosResponse) => void,
36 | ErrorFunc?: (error: any) => void){
37 | this.HttpPost('api/TaskReporter/GetTaskSourceSummary', isLoading, data, SuccessFunc, ErrorFunc)
38 | }
39 |
40 | GetTaskTimeSummary(data: IQueryPeopleTasks, isLoading: Ref,
41 | SuccessFunc?: (response: AxiosResponse) => void,
42 | ErrorFunc?: (error: any) => void){
43 | this.HttpPost('api/TaskReporter/GetTaskTimeSummary', isLoading, data, SuccessFunc, ErrorFunc)
44 | }
45 |
46 | }
47 |
48 |
49 | export {
50 | TaskReporterAPIHandler
51 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/always_grey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/always_grey.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/blue_wash_wall_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/blue_wash_wall_1.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/blue_wash_wall_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/blue_wash_wall_2.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/blue_wash_wall_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/blue_wash_wall_3.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/blue_wash_wall_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/blue_wash_wall_4.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/crossword.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/crossword.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/dark_wood.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/dark_wood.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/dust_scratches.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/dust_scratches.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/fabric_of_squares_gray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/fabric_of_squares_gray.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/footer_lodyas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/footer_lodyas.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/footer_lodyas2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/footer_lodyas2.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/forest/forest1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/forest/forest1.jpg
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/forest/forest2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/forest/forest2.jpg
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/grey_wash_wall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/grey_wash_wall.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/noisy_grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/noisy_grid.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/noisy_grid2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/noisy_grid2.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/pikachou.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/pikachou.gif
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/project_papper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/project_papper.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/triangular.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/triangular.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/vichy-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/vichy-dark.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/vichy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/vichy.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/whitey copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/whitey copy.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/whitey-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/whitey-light.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/whitey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/whitey.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/xv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/xv.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/img/xv_light_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/img/xv_light_blue.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/TimeTracker/TimeTracker/ClientApp/src/assets/logo.png
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/auth/SignInModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
53 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/card/GeneralCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 | {{titleIcon}}
11 | {{title}}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
61 |
62 |
65 |
66 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/clock/AnalogClock.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
22 |
23 |
24 |
44 |
45 |
50 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/clock/Clock.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
66 |
67 |
75 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/clock/DigitalClock.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ date }}
4 |
{{ time }}
5 |
6 |
7 |
8 |
58 |
59 |
97 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/img/SimpleImageEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
17 | mdi-delete
18 |
19 |
20 |
21 |
22 |
23 |
71 |
72 |
94 |
95 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/nav/NavGithubLink.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
21 |
22 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/nav/NavIndividualSettings.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
36 |
37 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/nav/NavItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{mdiIcon}}
4 |
5 |
6 |
7 |
8 |
9 |
30 |
31 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/nav/NavLinkItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ navDisplayName }}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
60 |
61 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/nav/NavProgress.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
25 |
26 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/nav/TwoColumnSidebarToggleIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
40 |
41 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/trackTask/taskForm/TaskDayLeaveCheckbox.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
54 |
55 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/trackTask/taskForm/TaskDayLeaveIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 | mdi-marker-cancel
9 | mdi-pencil
10 |
11 |
12 |
13 |
57 |
58 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/trackTask/taskForm/taskform.ts:
--------------------------------------------------------------------------------
1 |
2 | interface IAddRow{
3 | newIndex: number;
4 | }
5 |
6 | interface IRemoveRow{
7 | oldIndex: number;
8 | value: any;
9 | }
10 |
11 | interface IMoveRow{
12 | newIndex: number;
13 | oldIndex: number;
14 | }
15 |
16 | interface IUpdateCell{
17 | rowIndex: number;
18 | relatedKey: string;
19 | oldValue: any;
20 | newValue: any;
21 | }
22 |
23 | export {
24 | IAddRow,
25 | IRemoveRow,
26 | IMoveRow,
27 | IUpdateCell,
28 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/components/trackTask/toolbar/DateRangeSelector.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
56 |
57 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/configGtag.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 | import { VueConstructor } from 'vue'
4 | import VueGtag from "vue-gtag"
5 | import VueRouter from 'vue-router'
6 |
7 | function ConfigGtag(vue: VueConstructor, router: VueRouter) {
8 | const gaId = process.env.VUE_APP_GA_ID
9 | // console.log("check gtag", gaId)
10 | if (process.env.NODE_ENV === 'production' && gaId ) {
11 | vue.use(VueGtag, {
12 | config: {
13 | id: gaId
14 | },
15 | appName: 'Time Tracker',
16 | pageTrackerScreenviewEnabled: true,
17 | }, router)
18 | }
19 | }
20 |
21 | export default ConfigGtag
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/jctk-table-form.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'jctk-table-form/src/components/ShareVar'{
2 | interface ICellTypes{
3 | number: string;
4 | singleSelect: string;
5 | textarea: string;
6 | }
7 |
8 | interface IFormSettings{
9 | cellTypes: ICellTypes;
10 | }
11 |
12 | declare let FormSettings:IFormSettings
13 |
14 | export default FormSettings
15 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/main.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import './registerServiceWorker'
4 | import CreateRouter from './router/index'
5 | import store from './store'
6 | import VueCompositionAPI from '@vue/composition-api'
7 | import ConfigGtag from './configGtag'
8 |
9 | import vuetify from './vuetify'
10 |
11 | import 'jctk-table-form'
12 | import 'jctk-table-form/dist/jctk-table-form.css'
13 |
14 | // for icon
15 | import 'material-design-icons/iconfont/material-icons.css'
16 | import '@mdi/font/css/materialdesignicons.css'
17 |
18 | import 'animate.css/animate.min.css'
19 |
20 | Vue.config.productionTip = false
21 |
22 | Vue.use(VueCompositionAPI)
23 |
24 | const router = CreateRouter(store)
25 |
26 | ConfigGtag( Vue, router)
27 |
28 | new Vue({
29 | vuetify,
30 | router,
31 | store,
32 | render: h => h(App)
33 | }).$mount('#app')
34 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/authentication.ts:
--------------------------------------------------------------------------------
1 | import { AccountStatus } from './constants/authentication'
2 |
3 | interface IUserRole{
4 | id: number;
5 | codeName: string;
6 | displayName: string;
7 | }
8 |
9 | interface IClaims{
10 | guid: string;
11 | name: string;
12 | email: string;
13 | accountStatus: AccountStatus;
14 | userRoles: Array;
15 | }
16 |
17 | interface IAuthentication{
18 | isAuthenticated: boolean;
19 | claims: IClaims;
20 | }
21 |
22 | interface ILogin{
23 | email: string;
24 | name: string;
25 | password: string;
26 | }
27 |
28 | interface IUpdatePassword{
29 | currentPassword: string;
30 | password: string;
31 | }
32 |
33 | interface IUpdateAccounts{
34 | Guid: string;
35 | Name: string;
36 | IsUpdateName: boolean;
37 |
38 | AccountStatus: AccountStatus;
39 | IsUpdateAccountStatus: boolean;
40 |
41 | UserRoles: Array;
42 | IsUpdateUserRoles: boolean;
43 | }
44 |
45 | enum ValidationResults{
46 | ok,
47 | invalidPath,
48 | invalidAccountStatus,
49 | invalidAuthentication,
50 | invalidRole,
51 | logout,
52 | }
53 |
54 | function GetAccountRemind(accountStatus: AccountStatus){
55 | switch (accountStatus) {
56 | case AccountStatus.Uncheck:
57 | return "Please connect admin to activate your account."
58 | case AccountStatus.Approved:
59 | return ""
60 | case AccountStatus.Rejected:
61 | return "Your account is rejected."
62 | case AccountStatus.Suspend:
63 | return "Your account is suspended."
64 | default:
65 | return ""
66 | }
67 | }
68 |
69 | export {
70 | IUserRole,
71 | IClaims,
72 | IAuthentication,
73 | ILogin,
74 | IUpdatePassword,
75 | IUpdateAccounts,
76 | ValidationResults,
77 | GetAccountRemind,
78 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/charts.ts:
--------------------------------------------------------------------------------
1 | interface IDayCount{
2 | date: string;
3 | consumeTime: number;
4 | overtime: number;
5 | isLeave: boolean;
6 | }
7 |
8 | interface IEchartsPieRow{
9 | name: string;
10 | value: number;
11 | }
12 |
13 | interface ITaskTimeSummaryDetail{
14 | date: string;
15 | totalConsumeTime: number;
16 | totalOvertime: number;
17 | totalLeave: number;
18 | avgConsumeTime: number;
19 | avgOvertime: number;
20 | medianConsumeTime: number;
21 | medianOvertime: number;
22 | minConsumeTime: number;
23 | minOvertime: number;
24 | maxConsumeTime: number;
25 | maxOvertime: number;
26 | }
27 |
28 | export {
29 | IDayCount,
30 | IEchartsPieRow,
31 | ITaskTimeSummaryDetail,
32 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/constants/authentication.ts:
--------------------------------------------------------------------------------
1 | enum AccountStatus{
2 | Uncheck = 0, // 未審核用戶
3 | Approved = 1, // 合法用戶
4 | Rejected = 2, // 拒絕用戶
5 | Suspend = 3, // 停權用戶
6 | }
7 |
8 | enum UserRoles
9 | {
10 | Admin = 1,
11 | User = 2,
12 | }
13 |
14 | const accountStatusIcon = {
15 | 0: "mdi-account",
16 | 1: "mdi-account",
17 | 2: "mdi-account",
18 | 3: "mdi-account",
19 | }
20 |
21 | const accountStatusColor = {
22 | 0: "warning",
23 | 1: "primary",
24 | 2: "error",
25 | 3: "error",
26 | }
27 |
28 | function GetAccountStatusKey(){
29 | return Object.keys(AccountStatus).map(Number).filter( x => !Number.isNaN(x))
30 | }
31 |
32 | function GetUserRolesKey(){
33 | return Object.keys(UserRoles).map(Number).filter( x => !Number.isNaN(x))
34 | }
35 |
36 | export {
37 | accountStatusIcon,
38 | accountStatusColor,
39 | AccountStatus,
40 | UserRoles,
41 | GetAccountStatusKey,
42 | GetUserRolesKey,
43 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/constants/task.ts:
--------------------------------------------------------------------------------
1 | import FormSettings from 'jctk-table-form/src/components/ShareVar'
2 |
3 | import { ITaskOption, ITaskParameters } from "../tasks";
4 |
5 | const TransformedOptions = ( taskOptions: Array ) =>
6 | taskOptions.map( taskOption => ({ name: taskOption.displayName, value: taskOption.id }) ) || []
7 |
8 | const GetOptions = ( taskParameters: ITaskParameters ) =>({
9 | head:[{
10 | title: "時數",
11 | relatedKey: "consumeTime",
12 | cellType: FormSettings.cellTypes.number /*as string*/,
13 | style:{
14 | "width": "80px",
15 | "min-width": "80px",
16 | },
17 | options:{
18 | step: 0.5,
19 | min: 0.0,
20 | max: 12.0,
21 | },
22 | },{
23 | title: "工作類型",
24 | relatedKey: "taskType",
25 | cellType: FormSettings.cellTypes.singleSelect /*as string*/,
26 | style:{
27 | width: "160px",
28 | "min-width": "160px",
29 | },
30 | options: TransformedOptions(taskParameters.taskTypes),
31 | },{
32 | title: "工作來源",
33 | relatedKey: "taskSource",
34 | cellType: FormSettings.cellTypes.singleSelect /*as string*/,
35 | style:{
36 | width: "160px",
37 | "min-width": "160px",
38 | },
39 | options: TransformedOptions(taskParameters.taskSources),
40 | },{
41 | title: "工作名稱",
42 | relatedKey: "taskName",
43 | cellType: FormSettings.cellTypes.textarea /*as string*/,
44 | options:{
45 | maxLength: 256,
46 | isSuggestions: true,
47 | throttle: 250,
48 | },
49 | },{
50 | title: "工作內容",
51 | relatedKey: "taskContent",
52 | cellType: FormSettings.cellTypes.textarea /*as string*/,
53 | options:{
54 | maxLength: 256,
55 | isSuggestions: true,
56 | throttle: 250,
57 | },
58 | }]
59 | })
60 |
61 | export {
62 | GetOptions
63 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/constants/webSocket.ts:
--------------------------------------------------------------------------------
1 | enum WSMapCode{
2 | GetUserInfo = 0,
3 | TaskEditorCreateTask = 1,
4 | TaskEditorDeleteTasks = 2,
5 | TaskEditorUpdateTaskRowOrder = 3,
6 | TaskEditorUpdateTaskCol = 4,
7 | TaskEditorUpdateIsLeave = 5,
8 | }
9 |
10 | export {
11 | WSMapCode
12 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/nav.ts:
--------------------------------------------------------------------------------
1 | import { routeConfigs } from '@/router/routeConfigs'
2 | import { ValidateAuth } from '@/util/authValidation'
3 | import { ValidationResults } from '@/models/authentication'
4 | import { Store } from 'vuex/types/index'
5 | import { IStore } from './store'
6 |
7 | const allNavLeftItems = [ "TaskEditor", "TaskReporter", "TaskSummaryReporter" ]
8 | const allNavRightItems = [ "TrackSettings", "PermissionEditor" ]
9 |
10 | function FiltNavItems(candidates: Array, store: Store): Array{
11 | return candidates.filter(
12 | pathName => ValidateAuth(pathName as string, store, routeConfigs) == ValidationResults.ok)
13 | }
14 |
15 | export {
16 | allNavLeftItems,
17 | allNavRightItems,
18 |
19 | FiltNavItems,
20 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/notification.ts:
--------------------------------------------------------------------------------
1 | import ToastInterface from 'vue-toastification/dist/types/src/ts/interface';
2 |
3 | interface INotification{
4 | Notificate403: any;
5 | Notificate401: any;
6 | NotificateWSReconnected: any;
7 | NotificateWSReconnecting: any;
8 | NotificateWSClose: any;
9 | notificator: ReturnType | undefined,
10 | }
11 |
12 |
13 | export {
14 | INotification
15 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/pageIdle.ts:
--------------------------------------------------------------------------------
1 | enum IdleDetermineStates{
2 | ByPass,
3 | PageIdle,
4 | UserComfirm,
5 | Logout,
6 | }
7 |
8 | interface IPageIdle{
9 | startIdleTimestamp?: Date;
10 | startUserConfirmTimestamp?: Date;
11 | isShowUserConfirm: boolean;
12 | isShowLogOutNotification: boolean;
13 | idleTimes: number;
14 | idleDetermineStates: IdleDetermineStates;
15 | }
16 |
17 | export {
18 | IdleDetermineStates,
19 | IPageIdle
20 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/parameter.ts:
--------------------------------------------------------------------------------
1 | import { Ref } from "@vue/composition-api"
2 | import { AxiosResponse } from "axios"
3 |
4 | interface ITaskEntityOption{
5 | guid: string;
6 | codeName: string; // unique
7 | displayName: string;
8 | isActive: boolean;
9 | }
10 |
11 | interface IOptionMethods{
12 | GetOptions: (
13 | isLoading: Ref,
14 | SuccessFunc?: (response: AxiosResponse) => void,
15 | ErrorFunc?: (error: any) => void
16 | ) => void;
17 | CreateOptions: (
18 | data: Array,
19 | isLoading: Ref,
20 | SuccessFunc?: (response: AxiosResponse) => void,
21 | ErrorFunc?: (error: any) => void
22 | ) => void
23 | UpdateOptions: (
24 | data: Array,
25 | isLoading: Ref,
26 | SuccessFunc?: (response: AxiosResponse) => void,
27 | ErrorFunc?: (error: any) => void
28 | ) => void
29 | }
30 |
31 | export {
32 | ITaskEntityOption,
33 | IOptionMethods,
34 | }
35 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/period.ts:
--------------------------------------------------------------------------------
1 |
2 | interface IPeriod{
3 | guid: string;
4 | startDate: string;
5 | endDate: string;
6 | name: string;
7 | }
8 |
9 | export {
10 | IPeriod
11 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/routeConfigs.ts:
--------------------------------------------------------------------------------
1 | import { VueConstructor } from "vue";
2 |
3 | import { UserRoles } from '@/models/constants/authentication'
4 |
5 | interface IRouteConfigs{
6 | [key: string]: IRouteConfig;
7 | }
8 |
9 | interface IRouteConfig{
10 | path: string;
11 | name: string;
12 | component: VueConstructor | ( () => Promise );
13 | auth: IRouteAuth;
14 | nav: IRouteNav;
15 | }
16 |
17 | interface IRouteAuth{
18 | isRequiredAuthentication: boolean;
19 | isRequiredApprovedAccount: boolean;
20 | requiredRoles: Array;
21 | }
22 |
23 | interface IRouteNav{
24 | displayName: string;
25 | mdiIcon: string;
26 | }
27 |
28 | export {
29 | IRouteConfigs,
30 | IRouteConfig,
31 | IRouteAuth,
32 | IRouteNav,
33 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/store.ts:
--------------------------------------------------------------------------------
1 | import { IAuthentication } from './authentication'
2 | import { INotification } from './notification'
3 | import { IPageIdle } from './pageIdle'
4 | import { ITaskParameters } from './tasks'
5 | import { ITwoColumn } from './twoColumn'
6 | import { IWebSocket } from './webSocket'
7 |
8 |
9 | interface IStoreRoot{
10 | isLoading: boolean,
11 | tabGuid: string,
12 | userImage: string,
13 | }
14 |
15 | interface IStore extends IStoreRoot{
16 | authentication: IAuthentication,
17 | pageIdle: IPageIdle,
18 | taskParameters: ITaskParameters,
19 | notification: INotification,
20 | webSocket: IWebSocket,
21 | twoColumn: ITwoColumn,
22 | }
23 |
24 | export {
25 | IStoreRoot,
26 | IStore,
27 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/tasks.ts:
--------------------------------------------------------------------------------
1 |
2 | interface IDateRange{
3 | periodName: string;
4 | isUsedPeriod: boolean;
5 | startDate: string;
6 | endDate: string;
7 | }
8 |
9 | interface ITaskOption{
10 | id: number;
11 | guid: string;
12 | codeName: string;
13 | displayName: string;
14 | }
15 |
16 | interface ITaskParameters{
17 | taskTypes: Array;
18 | taskSources: Array;
19 | dayWorkLimitTime: number | undefined;
20 | }
21 |
22 | interface ITask{
23 | guid: string;
24 | displayOrder: number;
25 | date: string;
26 | consumeTime: number;
27 | taskType: number;
28 | taskSource: number;
29 | taskName: string;
30 | taskContent: string;
31 | }
32 |
33 | interface IDeleteTasks{
34 | ownerGuid: string;
35 | date: string;
36 | taskGuids: Array;
37 | }
38 |
39 | interface IUpdateIsLeave{
40 | ownerGuid: string;
41 | date: string;
42 | isLeave: boolean;
43 | }
44 |
45 | interface IUpdateTask{
46 | guid: string;
47 | displayOrder: number;
48 | date: string;
49 | consumeTime: number;
50 | taskType: ITaskOption;
51 | taskSource: ITaskOption;
52 | taskName: string;
53 | taskContent: string;
54 | }
55 |
56 | interface IUpdateTaskRowOrder{
57 | ownerGuid: string;
58 | guid: string; // task guid
59 | date: string;
60 | displayOrder: number;
61 | }
62 |
63 | interface IUpdateTaskCol{
64 | ownerGuid: string;
65 | guid: string; // task guid
66 | date: string;
67 | relatedKey: string;
68 | value: any;
69 | }
70 |
71 | interface IGetDaysDataResponseTask{
72 | guid: string;
73 | displayOrder: number;
74 | date: string;
75 | consumeTime: number;
76 | taskType: ITaskOption | number;
77 | taskSource: ITaskOption | number;
78 | taskName: string;
79 | taskContent: string;
80 | }
81 |
82 | interface IDayData{
83 | guid: string;
84 | date: string;
85 | isLeave: boolean;
86 | isFormClicked: boolean;
87 | formData: Array;
88 | }
89 |
90 | interface IGetDaysDataResponse{
91 | guid: string;
92 | date: string;
93 | isLeave: boolean;
94 | isFormClicked: boolean;
95 | formData: Array;
96 | }
97 |
98 | interface IQueryTasks{
99 | ownerGuid: string;
100 | startDate: string;
101 | endDate: string;
102 | }
103 |
104 | interface IQueryPeopleTasks{
105 | ownerGuids: Array;
106 | startDate: string;
107 | endDate: string;
108 | }
109 |
110 | interface ICreateTask{
111 | tasks: Array;
112 | ownerGuid: string;
113 | }
114 |
115 | export {
116 | IDateRange,
117 | ITaskOption,
118 | ITaskParameters,
119 | ITask,
120 | IDeleteTasks,
121 | IUpdateIsLeave,
122 | IUpdateTask,
123 | IUpdateTaskRowOrder,
124 | IUpdateTaskCol,
125 | IDayData,
126 | IGetDaysDataResponse,
127 | IQueryTasks,
128 | IQueryPeopleTasks,
129 | ICreateTask,
130 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/twoColumn.ts:
--------------------------------------------------------------------------------
1 | interface ITwoColumn{
2 | isShowSidebar: boolean;
3 | isActiveTwoColumn: boolean;
4 | }
5 |
6 | export {
7 | ITwoColumn
8 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/models/webSocket.ts:
--------------------------------------------------------------------------------
1 | import { WSHandler } from '@/api/webSocket';
2 | import { IQueryTasks } from './tasks';
3 |
4 | interface IWebSocket{
5 | wsHandler: WSHandler | undefined;
6 | taskEditorQueryTasks: IQueryTasks | undefined;
7 | }
8 |
9 | export {
10 | IWebSocket
11 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/registerServiceWorker.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | import { register } from 'register-service-worker'
4 |
5 | if (process.env.NODE_ENV === 'production') {
6 | register(`${process.env.BASE_URL}service-worker.js`, {
7 | ready () {
8 | console.log(
9 | 'App is being served from cache by a service worker.\n' +
10 | 'For more details, visit https://goo.gl/AFskqB'
11 | )
12 | },
13 | registered () {
14 | console.log('Service worker has been registered.')
15 | },
16 | cached () {
17 | console.log('Content has been cached for offline use.')
18 | },
19 | updatefound () {
20 | console.log('New content is downloading.')
21 | },
22 | updated () {
23 | console.log('New content is available; please refresh.')
24 | },
25 | offline () {
26 | console.log('No internet connection found. App is running in offline mode.')
27 | },
28 | error (error) {
29 | console.error('Error during service worker registration:', error)
30 | }
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter, { RouteConfig } from 'vue-router'
3 | import { Store } from 'vuex/types/index'
4 | import { routeConfigs } from './routeConfigs'
5 | import ConfigRouteValidation from './routeRoleValidation'
6 |
7 | Vue.use(VueRouter)
8 |
9 | const routes: Array = Object.values(routeConfigs).map( routeConfig => ({
10 | path: routeConfig.path,
11 | name: routeConfig.name,
12 | component: routeConfig.component,
13 | }))
14 |
15 | export default function(store: Store): VueRouter{
16 | const router = new VueRouter({
17 | mode: 'history',
18 | base: process.env.BASE_URL,
19 | routes
20 | })
21 |
22 | ConfigRouteValidation(router, store)
23 |
24 | return router
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/router/routeRoleValidation.ts:
--------------------------------------------------------------------------------
1 | import { VueRouter, Route } from 'vue-router/types/router'
2 | import { Store } from 'vuex/types/index'
3 |
4 | import { IStore } from '@/models/store'
5 | import { ValidateAuth } from '@/util/authValidation'
6 | import { ValidationResults } from '@/models/authentication'
7 | import { GetRedirectPath, routeConfigs } from './routeConfigs'
8 |
9 | import { ToastWarning } from '@/util/notification'
10 |
11 | function ConfigRouteValidation(router: VueRouter, store: Store ){
12 | router.beforeEach((to, from, next) => {
13 | if (from.path == to.path) {
14 | next()
15 | return
16 | }
17 |
18 | const result = ValidateAuth(to.name as string, store, routeConfigs)
19 | if( result != ValidationResults.ok ){
20 | // setTimeout( () => ToastWarning(store.state.notificator, `Insufficient permision found: ${ValidationResults[result]}`), 2000 )
21 | ToastWarning(`Insufficient permision found: ${ValidationResults[result]}`, store.state.notification.notificator)
22 | next(GetRedirectPath(result))
23 | return
24 | }
25 |
26 | next()
27 | })
28 |
29 | function HasQueryParams(route: Route) {
30 | return !!Object.keys(route.query).length
31 | }
32 |
33 | router.beforeEach((to, from, next) => {
34 | if( !HasQueryParams(to) && HasQueryParams(from) ){
35 | next({ name: to.name as string, query: from.query })
36 | } else {
37 | next()
38 | }
39 | })
40 | }
41 |
42 | export default ConfigRouteValidation
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/scss/example.scss:
--------------------------------------------------------------------------------
1 | // =================================================================================
2 | // midea query
3 | // =================================================================================
4 | @media (max-width: $mediaquery-width-sm) {
5 |
6 | }
7 |
8 | // Small devices (landscape phones, 576px and up)
9 | @media (min-width: $mediaquery-width-sm) and (max-width: $mediaquery-width-md) {
10 |
11 | }
12 |
13 | // Medium devices (tablets, 768px and up)
14 | @media (min-width: $mediaquery-width-md) and (max-width: $mediaquery-width-l) {
15 |
16 | }
17 |
18 | // Large devices (desktops, 992px and up)
19 | @media (min-width: $mediaquery-width-l) and (max-width: $mediaquery-width-xl) {
20 |
21 | }
22 |
23 | // Extra large devices (large desktops, 1200px and up)
24 | @media (min-width: $mediaquery-width-xl) {
25 |
26 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/scss/global_var.scss:
--------------------------------------------------------------------------------
1 | @import "./var";
2 |
3 | @include vm-global();
4 |
5 | hr.vm-test{
6 | margin: 0 25px;
7 | height: 500px;
8 | width: 1px;
9 | background: black;
10 | background: -webkit-gradient(linear, 180 180, 100% 0, from(white), to(white), color-stop(50%, black));
11 | }
12 |
13 | $alert-color: #922828;
14 |
15 | $tmp-color: rgba(165, 126, 126, 0.459);
16 | $tmp-color: #e6dcdc;
17 |
18 | $tmp-color: #922828;
19 | $tmp-color: #e27c7c;
20 |
21 | $tmp-color: #2c3e50;
22 | $tmp-color: #496586;
23 | $tmp-color: #f5f3f3;
24 |
25 | .flex-center{
26 | display: flex;
27 | justify-content: center;
28 | }
29 |
30 | .margin-center{
31 | margin: 0 auto;
32 | }
33 |
34 | $color-primary: #009688;
35 | $color-secondary: #00bcd4;
36 | $color-accent: #607d8b;
37 | $color-error: #ff5722;
38 | $color-warning: #ff9800;
39 | $color-info: #2196f3;
40 | $color-success: #4caf50;
41 | $color-title: #334b5c;
42 |
43 | .theme-table.theme--light.v-data-table tbody tr.v-data-table__selected{
44 | background: $color-accent;
45 | color: #d3d3d3;
46 |
47 | &:hover{
48 | background: $color-primary !important;
49 | color: #d3d3d3 !important;
50 | }
51 | }
52 |
53 | html {
54 | // background: $background;
55 | // background: red;
56 | // background-attachment: fixed;
57 |
58 | overflow: auto;
59 | overflow-y: auto !important;
60 |
61 | // @media (max-width: $mediaquery-width-sm) {
62 | // &::-webkit-scrollbar {
63 | // display: none;
64 | // }
65 | // }
66 |
67 | // @media (min-width: $mediaquery-width-sm) {
68 | &::-webkit-scrollbar
69 | {
70 | width: 14px;
71 | height: 12px;
72 | background-color: #eaeaea;
73 | }
74 |
75 | &::-webkit-scrollbar-thumb
76 | {
77 | border-radius: 10px;
78 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
79 | background-color: #d8d8d8;
80 |
81 | &:hover{
82 | background-color: #ecebeb;
83 | }
84 | }
85 | // }
86 | }
87 |
88 | .animate__animated.animate__fastest {
89 | -webkit-animation-duration: 0.25s;
90 | animation-duration: 0.25s;
91 | -webkit-animation-duration: 0.25s;
92 | animation-duration: 0.25s;
93 | }
94 |
95 | .user-image-nav{
96 | height: 48px;
97 | width: 48px;
98 | border-radius: 48px;
99 | }
100 |
101 | .user-image-big{
102 | height: 256px;
103 | width: 256px;
104 | border-radius: 256px;
105 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/shims-tsx.d.ts:
--------------------------------------------------------------------------------
1 | import Vue, { VNode } from 'vue'
2 |
3 | declare global {
4 | namespace JSX {
5 | // tslint:disable no-empty-interface
6 | interface Element extends VNode {}
7 | // tslint:disable no-empty-interface
8 | interface ElementClass extends Vue {}
9 | interface IntrinsicElements {
10 | [elem: string]: any;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import Vue from 'vue'
3 | export default Vue
4 | }
5 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/store/authentication.ts:
--------------------------------------------------------------------------------
1 | import { IAuthentication, IClaims } from '@/models/authentication'
2 | import { Module } from "vuex";
3 |
4 | const GetInitAuthentication = (): IAuthentication => ({
5 | isAuthenticated: false,
6 | claims: {
7 | guid: "",
8 | email: "",
9 | name: "",
10 | accountStatus: 0,
11 | userRoles: [],
12 | },
13 | })
14 |
15 | function SetClaims( state: IAuthentication, claims: IClaims ){
16 | Object.keys(claims).forEach( key => Reflect.set( state.claims, key, Reflect.get(claims, key) ) )
17 | }
18 |
19 | const module: Module = {
20 | namespaced: true,
21 | state: GetInitAuthentication(),
22 | mutations: {
23 | Init: ( state ) => {
24 | const initAuthentication = GetInitAuthentication()
25 | Object.keys(initAuthentication).forEach( key => Reflect.set( state, key, Reflect.get(initAuthentication, key) ) )
26 | SetClaims(state, state.claims)
27 | },
28 | SetIsAuthenticated: ( state, isAuthenticated: boolean ) => state.isAuthenticated = isAuthenticated,
29 | SetClaims: ( state, claims: IClaims ) => SetClaims(state, claims),
30 | SetEmail: ( state, email: string ) => state.claims.email = email,
31 | SetName: ( state, name: string ) => state.claims.name = name,
32 | },
33 | actions: {
34 | },
35 | modules: {
36 | }
37 | }
38 |
39 | export default module
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex, { Store } from 'vuex'
3 |
4 | import createMultiTabState from 'vuex-multi-tab-state'
5 |
6 | import { IStore, IStoreRoot } from '@/models/store'
7 | import authentication from './authentication'
8 | import pageIdle from './pageIdle'
9 | import taskParameters from './taskParameters'
10 | import notification from './notification'
11 | import webSocket from './webSocket'
12 | import twoColumn from './twoColumn'
13 | import { GetGuid } from '@/util/authentication'
14 |
15 | Vue.use(Vuex)
16 |
17 | export default new Vuex.Store({
18 | state: {
19 | isLoading: false,
20 | tabGuid: GetGuid(),
21 | userImage: "",
22 | },
23 | mutations: {
24 | TurnOnLoading: ( state ) => {
25 | state.isLoading = true
26 | },
27 | TurnOffLoading: ( state ) => {
28 | state.isLoading = false
29 | },
30 | SetUserImage: ( state, userImage: string ) => state.userImage = userImage
31 | },
32 | actions: {
33 | },
34 | modules: {
35 | taskParameters,
36 | authentication,
37 | pageIdle,
38 | notification,
39 | webSocket,
40 | twoColumn,
41 | },
42 | plugins: [
43 | createMultiTabState({
44 | statesPaths: ['authentication', 'pageIdle', 'userImage']
45 | }),
46 | ],
47 | }) as Store
48 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/store/notification.ts:
--------------------------------------------------------------------------------
1 | import { INotification } from '@/models/notification';
2 | import { ToastSuccess, ToastWarning } from '@/util/notification';
3 | import debounce from 'lodash.debounce';
4 | import Vue from 'vue'
5 | import { TYPE } from 'vue-toastification/dist/types/src/ts/constants';
6 | import ToastInterface from 'vue-toastification/dist/types/src/ts/interface';
7 | import { ToastOptions } from 'vue-toastification/dist/types/src/types';
8 | import { Module } from "vuex";
9 |
10 | const GetInitState = (): INotification => ({
11 | notificator: undefined,
12 | Notificate401: undefined,
13 | Notificate403: undefined,
14 | NotificateWSReconnected: undefined,
15 | NotificateWSReconnecting: undefined,
16 | NotificateWSClose: undefined,
17 | })
18 |
19 | const GetDebounceNoter = ( state: INotification, message: string, debouncTime = 1000, option?: (ToastOptions & {
20 | type?: TYPE.WARNING | undefined;}) | undefined) =>
21 | debounce( () => ToastWarning( message, state.notificator, option ), debouncTime )
22 |
23 | const SetNotificate401 = (state: INotification) =>
24 | state.Notificate401 = GetDebounceNoter( state,
25 | `Sorry! You don't have sufficient permission. You will be signed out automatically.`)
26 |
27 | const SetNotificate403 = (state: INotification) =>
28 | state.Notificate403 = GetDebounceNoter( state,
29 | `Sorry! You don't have sufficient permission.`)
30 |
31 | const SetNotificateWSReconnected = (state: INotification) =>
32 | state.NotificateWSReconnected = debounce( () => ToastSuccess(
33 | `The connection is restored successfully.`, state.notificator ), 500 )
34 |
35 | const SetNotificateWSReconnecting = (state: INotification) =>
36 | state.NotificateWSReconnecting = GetDebounceNoter( state,
37 | `Sorry! The connection is unstable now. Please wait for the connection.`, 500)
38 |
39 | const SetNotificateWSClose = (state: INotification) =>
40 | state.NotificateWSClose = GetDebounceNoter( state,
41 | `Sorry! The connection is break unpredictably. Your operation will not be executing. Please refresh the browser to continue.`, 500, { timeout: false })
42 |
43 | const module: Module = {
44 | namespaced: true,
45 | state: GetInitState(),
46 | mutations: {
47 | Init: ( state ) => {
48 | const initState = GetInitState()
49 | Vue.set(state, "notificator", initState.notificator)
50 | },
51 | SetNotificator: ( state, notificator: ReturnType ) => {
52 | state.notificator = notificator
53 | SetNotificate401( state )
54 | SetNotificate403( state )
55 | SetNotificateWSReconnected( state )
56 | SetNotificateWSReconnecting( state )
57 | SetNotificateWSClose( state )
58 | },
59 | },
60 | actions: {
61 | },
62 | modules: {
63 | }
64 | }
65 |
66 | export default module
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/store/pageIdle.ts:
--------------------------------------------------------------------------------
1 | import { IdleDetermineStates, IPageIdle } from '@/models/pageIdle'
2 | import { Module } from 'vuex/types/index'
3 |
4 | const GetInitPageIdle = (): IPageIdle => ({
5 | startIdleTimestamp: undefined,
6 | startUserConfirmTimestamp: undefined,
7 | isShowUserConfirm: false,
8 | isShowLogOutNotification: false,
9 | idleTimes: 0,
10 | idleDetermineStates: IdleDetermineStates.ByPass,
11 | })
12 |
13 | const module: Module = {
14 | namespaced: true,
15 | state: GetInitPageIdle(),
16 | getters:{
17 | },
18 | mutations: {
19 | Init: (state) => {
20 | const initPageIdle = GetInitPageIdle()
21 | Object.keys(initPageIdle).forEach( key => Reflect.set( state, key, Reflect.get(initPageIdle, key) ) )
22 | },
23 | SetStartIdleTimestamp: (state, timeStamp: Date) => state.startIdleTimestamp = timeStamp,
24 | SetStartUserConfirmTimestamp: (state, timeStamp: Date) => state.startUserConfirmTimestamp = timeStamp,
25 | SetIsShowUserConfirm: (state, isShowUserConfirm: boolean) => state.isShowUserConfirm = isShowUserConfirm,
26 | SetIsShowLogOutNotification: (state, isShowLogOutNotification: boolean) => state.isShowLogOutNotification = isShowLogOutNotification,
27 | SetIdleTimes: (state, idleTimes: number) => state.idleTimes = idleTimes,
28 | SetIdleDetermineStates: ( state, idleDetermineStates: IdleDetermineStates ) => state.idleDetermineStates = idleDetermineStates,
29 | },
30 | actions: {
31 | },
32 | modules: {
33 | }
34 | }
35 |
36 | export default module
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/store/taskParameters.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { ITaskOption, ITaskParameters } from '@/models/tasks';
3 | import { Module } from "vuex";
4 |
5 | const TASKTYPES = "taskTypes"
6 | const TASKSOURCES = "taskSources"
7 | const DAYWORKLIMITTIME = "dayWorkLimitTime"
8 |
9 | const GetInitState = (): ITaskParameters => ({
10 | taskTypes: [],
11 | taskSources: [],
12 | dayWorkLimitTime: undefined,
13 | })
14 |
15 | const SetOptions = ( state: ITaskParameters, tasktype: string, taskOptions: Array ) => {
16 | taskOptions.sort( ( x, y ) => {
17 | if (x.displayName < y.displayName) {
18 | return -1
19 | }
20 | if (x.displayName > y.displayName) {
21 | return 1
22 | }
23 | return 0
24 | }).forEach( (option, idx) => option.id = idx )
25 | Vue.set(state, tasktype, taskOptions)
26 | }
27 |
28 | const module: Module = {
29 | namespaced: true,
30 | state: GetInitState(),
31 | mutations: {
32 | Init: ( state ) => {
33 | const initState = GetInitState()
34 | Vue.set(state, TASKTYPES, initState.taskTypes)
35 | Vue.set(state, TASKSOURCES, initState.taskSources)
36 | Vue.set(state, DAYWORKLIMITTIME, initState.dayWorkLimitTime)
37 | },
38 | SetTaskTypes: ( state, taskTypes: Array ) => SetOptions(state, TASKTYPES, taskTypes),
39 | SetTaskSources: ( state, taskSources: Array ) => SetOptions(state, TASKSOURCES, taskSources),
40 | SetDayWorkLimitTime: ( state, dayWorkLimitTime: number ) => state.dayWorkLimitTime = dayWorkLimitTime,
41 | },
42 | actions: {
43 | },
44 | modules: {
45 | }
46 | }
47 |
48 | export default module
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/store/twoColumn.ts:
--------------------------------------------------------------------------------
1 | import { ITwoColumn } from '@/models/twoColumn'
2 | import { Module } from 'vuex/types/index'
3 |
4 | const GetInitState = (): ITwoColumn => ({
5 | isShowSidebar: false,
6 | isActiveTwoColumn: false,
7 | })
8 |
9 | const module: Module = {
10 | namespaced: true,
11 | state: GetInitState(),
12 | mutations: {
13 | Init: ( state ) => {
14 | const initState = GetInitState()
15 | Object.keys(initState).forEach( key => Reflect.set( state, key, Reflect.get(initState, key) ) )
16 | },
17 | ActiveSidebar: ( state ) => state.isShowSidebar = true,
18 | DisActiveSidebar: ( state ) => state.isShowSidebar = false,
19 | ToggleSidebar: ( state ) => state.isShowSidebar = !state.isShowSidebar,
20 | ActiveTwoColumn: ( state ) => state.isActiveTwoColumn = true,
21 | DisActiveTwoColumn: ( state ) => state.isActiveTwoColumn = false,
22 | },
23 | actions: {
24 | },
25 | modules: {
26 | }
27 | }
28 |
29 | export default module
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/store/webSocket.ts:
--------------------------------------------------------------------------------
1 | import { WSHandler } from '@/api/webSocket'
2 | import { IQueryTasks } from '@/models/tasks'
3 | import { IWebSocket } from '@/models/webSocket'
4 | import Vue from 'vue'
5 | import { Module } from 'vuex/types/index'
6 |
7 | const GetInitState = (): IWebSocket => ({
8 | wsHandler: undefined,
9 | taskEditorQueryTasks: undefined,
10 | })
11 |
12 | const module: Module = {
13 | namespaced: true,
14 | state: GetInitState(),
15 | mutations: {
16 | Init: ( state ) => {
17 | const initState = GetInitState()
18 | Vue.set(state, "wsHandler", initState.wsHandler)
19 | },
20 | SetWSHandler: ( state, wsHandler: WSHandler ) => {
21 | state.wsHandler = wsHandler
22 | },
23 | SetTaskEditorGroup: ( state, queryTasks: IQueryTasks ) => {
24 | Vue.set(state, "taskEditorQueryTasks", queryTasks)
25 | },
26 | },
27 | actions: {
28 | },
29 | modules: {
30 | }
31 | }
32 |
33 | export default module
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/util/authValidation.ts:
--------------------------------------------------------------------------------
1 | import { ValidationResults } from '@/models/authentication'
2 | import { Store } from 'vuex/types/index'
3 | import { AccountStatus } from '../models/constants/authentication'
4 | import { IRouteConfigs } from '../models/routeConfigs'
5 | import { IStore } from '../models/store'
6 |
7 | function ValidatePathExists(pathName: string, store: Store, routeConfigs: IRouteConfigs){
8 | const isFoundPath = pathName in routeConfigs
9 | if( !isFoundPath ){
10 | return ValidationResults.invalidPath
11 | }
12 |
13 | return ValidationResults.ok
14 | }
15 |
16 | function ValidateAccountStatus(pathName: string, store: Store, routeConfigs: IRouteConfigs){
17 | const isRequiredApprovedAccount = routeConfigs[pathName].auth.isRequiredApprovedAccount
18 | const isAccountApproved = store.state.authentication.claims.accountStatus == AccountStatus.Approved
19 | if( isRequiredApprovedAccount && !isAccountApproved ){
20 | return ValidationResults.invalidAccountStatus
21 | }
22 |
23 | return ValidationResults.ok
24 | }
25 |
26 | function ValidateAuthentication(pathName: string, store: Store, routeConfigs: IRouteConfigs){
27 | const isRequiredAuthentication = routeConfigs[pathName].auth.isRequiredAuthentication
28 | const isAuthenticated = store.state.authentication.isAuthenticated
29 | if( isRequiredAuthentication && !isAuthenticated ){
30 | return ValidationResults.invalidAuthentication
31 | }
32 |
33 | return ValidationResults.ok
34 | }
35 |
36 | function ValidateRole(pathName: string, store: Store, routeConfigs: IRouteConfigs){
37 | const requiredRoles = routeConfigs[pathName].auth.requiredRoles
38 | const userRoleIds = store.state.authentication.claims.userRoles.map( x => x.id )
39 |
40 | if( requiredRoles.length == 0 ){
41 | return ValidationResults.ok
42 | }
43 |
44 | if( requiredRoles.filter( allowRole => userRoleIds.includes(allowRole) ).length !== requiredRoles.length ){
45 | return ValidationResults.invalidRole
46 | }
47 |
48 | return ValidationResults.ok
49 | }
50 |
51 | function ValidateAuth(pathName: string, store: Store, routeConfigs: IRouteConfigs){
52 | const chaninedValidater: Array<(pathName: string, store: Store, routeConfigs: IRouteConfigs) => ValidationResults> = [
53 | ValidatePathExists,
54 | ValidateAccountStatus,
55 | ValidateAuthentication,
56 | ValidateRole,
57 | ]
58 |
59 | for (const Validater of chaninedValidater) {
60 | const result = Validater(pathName, store, routeConfigs)
61 | if( result != ValidationResults.ok ){
62 | return result
63 | }
64 | }
65 | return ValidationResults.ok
66 | }
67 |
68 | export {
69 | ValidatePathExists,
70 | ValidateAccountStatus,
71 | ValidateAuthentication,
72 | ValidateRole,
73 | ValidateAuth,
74 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/util/authentication.ts:
--------------------------------------------------------------------------------
1 | // function GetGuid() {
2 | // return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
3 | // (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
4 | // );
5 | // }
6 |
7 | import { IClaims } from "@/models/authentication";
8 |
9 | function GetGuid() {
10 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
11 | const r = Math.random() * 16 | 0,
12 | v = c == 'x' ? r : (r & 0x3 | 0x8);
13 | return v.toString(16);
14 | });
15 | }
16 |
17 | const GetGuids = ( users: Array) => users.map( selectedUser => selectedUser.guid )
18 |
19 | const IsEqualUsers = ( left: Array, right: Array ) => {
20 | const a = new Set(left)
21 | const b = new Set(right)
22 | const intersection = new Set([ ...a].filter( x => b.has(x) ))
23 | return a.size == intersection.size && b.size == intersection.size
24 | }
25 |
26 | export {
27 | GetGuid,
28 | GetGuids,
29 | IsEqualUsers,
30 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/util/breakPoint.ts:
--------------------------------------------------------------------------------
1 | // import { computed } from '@vue/composition-api'
2 | // import { useWindowSize } from 'vue-use-web'
3 |
4 | enum ScreenSize{
5 | xl,
6 | lg,
7 | md,
8 | sm,
9 | xs,
10 | }
11 |
12 | function GetSize(size: number) {
13 |
14 | if( 1904 <= window.innerWidth ){
15 | return ScreenSize.xl
16 | }else if( 1264 <= window.innerWidth && window.innerWidth < 1904 ){
17 | return ScreenSize.lg
18 | }else if( 960 <= window.innerWidth && window.innerWidth < 1264 ){
19 | return ScreenSize.md
20 | }else if( 600 <= window.innerWidth && window.innerWidth < 960 ){
21 | return ScreenSize.sm
22 | }else{
23 | return ScreenSize.xs
24 | }
25 | }
26 |
27 | // const GetScreenSize = () => computed( () => {
28 | // const { width, height } = useWindowSize({
29 | // throttleMs: 100
30 | // });
31 |
32 | // return GetSize(width.value)
33 | // })
34 |
35 | export {
36 | ScreenSize,
37 | GetSize,
38 | // GetScreenSize,
39 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/util/components/layout/ResponsiveElement.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
89 |
90 |
93 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/util/components/transition/RippleTransition.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
72 |
73 |
76 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/util/components/transition/RippleTransitionFlip.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
57 |
58 |
61 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/util/components/transition/SimpleTransition.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
44 |
45 |
47 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/util/notification.ts:
--------------------------------------------------------------------------------
1 | import { PluginOptions, ToastOptions } from "vue-toastification/dist/types/src/types"
2 | import ToastInterface from "vue-toastification/dist/types/src/ts/interface"
3 | import { TYPE, POSITION } from "vue-toastification/dist/types/src/ts/constants";
4 |
5 | function ToastSuccess(message: string, toast?: ReturnType, option?: (ToastOptions & {
6 | type?: TYPE.SUCCESS | undefined;
7 | }) | undefined){
8 | const mergedOption = {
9 | timeout: 7 * 1000,
10 | ...option
11 | } as PluginOptions
12 | return toast?.success(message, mergedOption)
13 | }
14 |
15 | function ToastWarning(message: string, toast?: ReturnType, option?: (ToastOptions & {
16 | type?: TYPE.WARNING | undefined;
17 | }) | undefined){
18 | const mergedOption = {
19 | timeout: 7 * 1000,
20 | ...option
21 | } as PluginOptions
22 | return toast?.warning(message, mergedOption)
23 | }
24 |
25 | function ToastError(message: string, toast?: ReturnType, option?: (ToastOptions & {
26 | type?: TYPE.WARNING | undefined;
27 | }) | undefined){
28 | const mergedOption = {
29 | timeout: 7 * 1000,
30 | ...option
31 | } as PluginOptions
32 | return toast?.error(message, mergedOption)
33 | }
34 |
35 | export {
36 | ToastSuccess,
37 | ToastWarning,
38 | ToastError,
39 | }
40 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/util/period.ts:
--------------------------------------------------------------------------------
1 | import moment from "moment"
2 | import { ValidateDate } from "./taskDate"
3 |
4 | function IsDisabledSubmit(name: string, startDate: string, endDate: string, isLoading: boolean) {
5 | if( name.length == 0 || startDate.length == 0 || endDate.length == 0) {
6 | return true
7 | }
8 |
9 | if( ValidateDate(startDate) !== true || ValidateDate(endDate) !== true ) {
10 | return true
11 | }
12 |
13 | if( moment(startDate) > moment(endDate) ){
14 | return true
15 | }
16 |
17 | if( isLoading ){
18 | return true
19 | }
20 |
21 | return false
22 | }
23 |
24 | export {
25 | IsDisabledSubmit
26 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/util/taskDate.ts:
--------------------------------------------------------------------------------
1 | import moment from 'moment'
2 |
3 | const dateFormat = "YYYY-MM-DD"
4 |
5 | function GetYesterday() {
6 | return moment().subtract(1, 'days').format(dateFormat)
7 | }
8 |
9 | function GetToday(){
10 | return moment().format(dateFormat);
11 | }
12 |
13 | function ValidateDate(value: string | undefined){
14 | return moment(value, dateFormat, true).isValid() || `Please input ${dateFormat}`
15 | }
16 |
17 | function FormatDate(value: string, format = dateFormat) {
18 | return moment(value).isValid() ? moment(value).format(format) : ""
19 | }
20 |
21 | function GetDateRange(startDate: string | undefined, endDate: string | undefined): Array {
22 | if( ValidateDate(startDate) !== true || ValidateDate(endDate) !== true ){
23 | return []
24 | }
25 |
26 | const dates = []
27 | const currentDate = moment(startDate).add(-1, 'days').startOf('day')
28 | const lastDate = moment(endDate).add(1, 'days').startOf('day')
29 |
30 | while(currentDate.add(1, 'days').diff(lastDate) < 0) {
31 | dates.push(currentDate.clone().format(dateFormat))
32 | }
33 |
34 | return dates
35 | }
36 |
37 | export {
38 | dateFormat,
39 | GetYesterday,
40 | GetToday,
41 | ValidateDate,
42 | FormatDate,
43 | GetDateRange,
44 | }
45 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/util/taskParameters.ts:
--------------------------------------------------------------------------------
1 | import { ITaskOption } from "@/models/tasks"
2 |
3 | const GetOptionIdByOption = ( storeTasksOptions: Array, value: ITaskOption ) => storeTasksOptions.find( x => x.guid === value.guid )?.id
4 | const GetOptionIdByGuid = ( storeTasksOptions: Array, value: string ) => storeTasksOptions.find( x => x.guid === value )?.id
5 | const GetOptionById = ( storeTasksOptions: Array, value: number ) => storeTasksOptions.find( x => x.id === value )
6 | const GetOptionGuidById = ( storeTasksOptions: Array, value: number ) => storeTasksOptions.find( x => x.id === value )?.guid
7 |
8 | export {
9 | GetOptionIdByOption,
10 | GetOptionIdByGuid,
11 | GetOptionById,
12 | GetOptionGuidById,
13 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/views/IndividualSettings.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
29 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/views/PermissionEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
80 |
81 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/views/Registration.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
29 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/views/TaskSummaryReporter.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/views/layouts/Center.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/views/layouts/Container.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
20 |
21 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/views/layouts/Tab.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 | {{title.icon}} {{title.text}}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
31 |
32 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/vue-echarts.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'vue-echarts' {
2 | import ECharts from 'vue-echarts'
3 | export default ECharts
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/vuetify-image-input.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'vuetify-image-input' {
2 | import VImageInput from 'vuetify-image-input'
3 | export default VImageInput
4 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/src/vuetify.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuetify from 'vuetify'
3 | import 'vuetify/dist/vuetify.min.css'
4 | import colors from 'vuetify/es5/util/colors'
5 | import { Iconfont } from 'vuetify/types/services/icons'
6 |
7 | Vue.use(Vuetify)
8 |
9 | const opts = {
10 | theme: {
11 | themes:{
12 | light:{
13 | // primary: colors.teal.base,
14 | // secondary: colors.cyan.base,
15 | // accent: colors.red.base,
16 | // error: colors.purple.base,
17 | // warning: colors.blue.base,
18 | // info: colors.deepPurple.base,
19 | // success: colors.lightGreen.base
20 |
21 | primary: colors.teal.base,
22 | secondary: colors.cyan.base,
23 | accent: colors.blueGrey.base,
24 | error: colors.deepOrange.base,
25 | warning: colors.orange.base,
26 | info: colors.blue.base,
27 | success: colors.green.base
28 | },
29 | }
30 |
31 | },
32 | }
33 |
34 | export default new Vuetify(opts)
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/tests/unit/example.spec.ts:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils'
2 | import HelloWorld from '@/components/HelloWorld.vue'
3 |
4 | describe('HelloWorld.vue', () => {
5 | it('renders props.msg when passed', () => {
6 | const msg = 'new message'
7 | const wrapper = shallowMount(HelloWorld, {
8 | propsData: { msg }
9 | })
10 | expect(wrapper.text()).toMatch(msg)
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "strict": true,
6 | "jsx": "preserve",
7 | "importHelpers": true,
8 | "moduleResolution": "node",
9 | "skipLibCheck": true,
10 | "resolveJsonModule": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "sourceMap": true,
14 | "baseUrl": ".",
15 | "types": [
16 | "webpack-env",
17 | "jest"
18 | ],
19 | "paths": {
20 | "@/*": [
21 | "src/*"
22 | ]
23 | },
24 | "lib": [
25 | "esnext",
26 | "dom",
27 | "dom.iterable",
28 | "scripthost"
29 | ]
30 | },
31 | "include": [
32 | "src/**/*.ts",
33 | "src/**/*.tsx",
34 | "src/**/*.vue",
35 | "tests/**/*.ts",
36 | "tests/**/*.tsx"
37 | ],
38 | "exclude": [
39 | "node_modules"
40 | ]
41 | }
42 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/vue.config.js:
--------------------------------------------------------------------------------
1 | // vue.config.js
2 | const webpack = require("webpack");
3 | const path = require("path");
4 |
5 | module.exports = {
6 | // options...
7 | // publicPath: process.env.NODE_ENV === "production"? "http://1.2.3.4:8080/assets" : "/",
8 | // publicPath: process.env.NODE_ENV === "production"? "/assets" : "/",
9 | publicPath:
10 | process.env.NODE_ENV === "production"
11 | ? "/"
12 | : "/",
13 | configureWebpack: {
14 | plugins: [
15 | new webpack.ProvidePlugin({
16 | $: "jquery",
17 | jQuery: "jquery",
18 | "windows.jQuery": "jquery"
19 | })
20 | ],
21 | },
22 | css: {
23 | loaderOptions: {
24 | sass: {
25 | prependData: `@import "~@/scss/global_var.scss";`
26 | }
27 | },
28 | sourceMap: true
29 | },
30 | transpileDependencies: [
31 | 'vue-echarts',
32 | 'resize-detector'
33 | ]
34 | };
35 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/ClientApp/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | module: {
3 | rules: [
4 | {
5 | test: /\.s(c|a)ss$/,
6 | use: [
7 | 'vue-style-loader',
8 | 'css-loader',
9 | {
10 | loader: 'sass-loader',
11 | // Requires sass-loader@^7.0.0
12 | options: {
13 | implementation: require('sass'),
14 | indentedSyntax: true // optional
15 | },
16 | // Requires sass-loader@^8.0.0
17 | options: {
18 | implementation: require('sass'),
19 | sassOptions: {
20 | indentedSyntax: true // optional
21 | },
22 | },
23 | },
24 | ],
25 | },
26 | ],
27 | }
28 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Controllers/ImageController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.EntityFrameworkCore;
7 | using TimeTracker.DAL;
8 | using TimeTracker.DAL.DBModels.Auth;
9 | using TimeTracker.DAL.DBModels.Task;
10 | using TimeTracker.DAL.Models;
11 | using TimeTracker.Helper.Auth;
12 | using TimeTracker.Helper.Extensions;
13 | using TimeTracker.Models.Period;
14 |
15 | namespace TimeTracker.Controllers
16 | {
17 | [Route("api/[controller]/[action]")]
18 | [ApiController]
19 | [Authorize]
20 | public class ImageController : ControllerBase
21 | {
22 | protected readonly ApplicationDbContext _context;
23 |
24 | public ImageController(ApplicationDbContext applicationDbContext)
25 | {
26 | this._context = applicationDbContext;
27 | }
28 |
29 | protected UserImage _GetUserImage()
30 | {
31 | int? id = User.GetUserId();
32 | return id == null ? null : this._context.UserImage.SingleOrDefault(x => x.User.Id == id);
33 | }
34 |
35 | protected void _CreateUserImage(string image)
36 | {
37 | int? userId = User.GetUserId();
38 | this._context.UserImage.Add(new UserImage((int)userId, image));
39 | this._context.SaveChanges();
40 | }
41 |
42 | protected void _UpdateUserImage(UserImage userImage, string image)
43 | {
44 | userImage.Image = image;
45 | userImage.SetUpdatedDate();
46 | this._context.SaveChanges();
47 | }
48 |
49 | [HttpPost]
50 | public IActionResult GetImage()
51 | {
52 | var result = this._GetUserImage();
53 |
54 | return Ok( result == null ? "" : result.Image );
55 | }
56 |
57 | [HttpPost]
58 | public IActionResult CreateOrUpdateImage(UserImage tmpUserImage)
59 | {
60 | string image = tmpUserImage.Image;
61 | var userImage = this._GetUserImage();
62 |
63 | if (userImage == null)
64 | {
65 | this._CreateUserImage(image);
66 | }
67 | else
68 | {
69 | this._UpdateUserImage(userImage, image);
70 | }
71 |
72 | return Ok();
73 | }
74 |
75 | [HttpPost]
76 | public IActionResult DeleteImage()
77 | {
78 | var userImage = this._GetUserImage();
79 | if (userImage == null)
80 | {
81 | return Ok();
82 | }
83 |
84 | this._context.UserImage.Remove(userImage);
85 | this._context.SaveChanges();
86 |
87 | return Ok();
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Controllers/PeriodController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.EntityFrameworkCore;
7 | using TimeTracker.DAL;
8 | using TimeTracker.DAL.DBModels.Task;
9 | using TimeTracker.DAL.Models;
10 | using TimeTracker.Helper.Auth;
11 | using TimeTracker.Models.Period;
12 |
13 | namespace TimeTracker.Controllers
14 | {
15 | [Route("api/[controller]/[action]")]
16 | [ApiController]
17 | [Authorize]
18 | public class PeriodController : ControllerBase
19 | {
20 | protected readonly ApplicationDbContext _context;
21 |
22 | public PeriodController(ApplicationDbContext applicationDbContext)
23 | {
24 | this._context = applicationDbContext;
25 | }
26 |
27 | [HttpPost]
28 | [AuthorizeRole(UserRoles.User)]
29 | public IActionResult GetPeriods()
30 | {
31 | var result = this._context.Period.Select(x => x).AsNoTracking();
32 | return Ok(result);
33 | }
34 |
35 | [HttpPost]
36 | [AuthorizeRole(UserRoles.Admin)]
37 | public IActionResult CreatePeriods(IEnumerable periods)
38 | {
39 | foreach (var period in periods)
40 | {
41 | period.Guid = Guid.NewGuid();
42 | }
43 |
44 | this._context.Period.AddRange(periods);
45 | this._context.SaveChanges();
46 |
47 | var result = periods.Select(x => x.Guid);
48 | return Ok(result);
49 | }
50 |
51 | [HttpPost]
52 | [AuthorizeRole(UserRoles.Admin)]
53 | public IActionResult UpdatePeriods(IEnumerable queryPeriods)
54 | {
55 | bool isUpdate = false;
56 | foreach (var queryPeriod in queryPeriods)
57 | {
58 | var period = this._context.Period.Where(x => x.Guid == queryPeriod.Guid).SingleOrDefault();
59 |
60 | if (period != null)
61 | {
62 | period.StartDate = queryPeriod.StartDate;
63 | period.EndDate = queryPeriod.EndDate;
64 | period.Name= queryPeriod.Name;
65 | period.SetUpdatedDate();
66 | isUpdate = true;
67 | }
68 | }
69 |
70 | if (isUpdate)
71 | {
72 | this._context.SaveChanges();
73 | }
74 | return Ok();
75 | }
76 |
77 | [HttpPost]
78 | [AuthorizeRole(UserRoles.Admin)]
79 | public IActionResult DeletePeriods(IEnumerable queryPeriods)
80 | {
81 | var targetGuids = queryPeriods.Select(x => x.Guid);
82 | var targetPeriods = this._context.Period.Where(x => targetGuids.Contains(x.Guid));
83 |
84 | if (targetPeriods != null)
85 | {
86 | this._context.Period.RemoveRange(targetPeriods);
87 | this._context.SaveChanges();
88 | }
89 |
90 | return Ok();
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Controllers/TaskEditorController.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Microsoft.AspNetCore.Authorization;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Microsoft.EntityFrameworkCore;
6 | using TimeTracker.DAL;
7 | using TimeTracker.DAL.Models;
8 | using TimeTracker.Helper.Auth;
9 | using TimeTracker.Models;
10 | using TimeTracker.Models.Task;
11 |
12 | namespace TimeTracker.Controllers
13 | {
14 | [Route("api/[controller]/[action]")]
15 | [ApiController]
16 | [Authorize]
17 | [AuthorizeRole(UserRoles.User)]
18 | public class TaskEditorController : ControllerBase
19 | {
20 | protected readonly ApplicationDbContext _context;
21 | protected readonly TaskHandler _taskHandler;
22 |
23 | public TaskEditorController(ApplicationDbContext applicationDbContext, TaskHandler taskHandler)
24 | {
25 | this._context = applicationDbContext;
26 | this._taskHandler = taskHandler;
27 | }
28 |
29 | [HttpPost]
30 | public IActionResult GetAccounts()
31 | {
32 | var accounts = this._context.User.Where(x => x.AccountStatus == AccountStatus.Approved).AsNoTracking().ToList();
33 | var returnData = new List();
34 | foreach (var account in accounts)
35 | {
36 | returnData.Add(new ApprovedUser(account));
37 | }
38 | return Ok(returnData);
39 | }
40 |
41 | [HttpPost]
42 | public IActionResult GetDaysData(QueryTasks queryTasks)
43 | {
44 | var result = this._taskHandler.GetDaysData(queryTasks);
45 | return Ok(result);
46 | }
47 |
48 | [HttpPost]
49 | public IActionResult CreateTask(CreateTask createTask)
50 | {
51 | var taskDays = this._taskHandler.CreateAndSelectTaskDay(createTask.Tasks, createTask.OwnerGuid);
52 |
53 | this._taskHandler.CreateTasks(createTask.Tasks, taskDays);
54 |
55 | var tasksGuids = createTask.Tasks.Select(x => x.Guid).ToList();
56 |
57 | return Ok(tasksGuids);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Controllers/TaskReporterController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.Extensions.Logging;
4 | using System.Linq;
5 | using TimeTracker.DAL;
6 | using TimeTracker.DAL.Models;
7 | using TimeTracker.Helper.Auth;
8 | using TimeTracker.Models.Task;
9 |
10 | namespace TimeTracker.Controllers
11 | {
12 | [Route("api/[controller]/[action]")]
13 | [ApiController]
14 | [Authorize]
15 | [AuthorizeRole(UserRoles.User)]
16 | public class TaskReporterController : ControllerBase
17 | {
18 | protected readonly ApplicationDbContext _context;
19 | protected readonly TaskReportHandler _taskReportHandler;
20 | private readonly ILogger _logger;
21 |
22 | public TaskReporterController(ApplicationDbContext applicationDbContext, TaskReportHandler taskReportHandler, ILogger logger)
23 | {
24 | this._context = applicationDbContext;
25 | this._taskReportHandler = taskReportHandler;
26 | _logger = logger;
27 | }
28 |
29 | [HttpPost]
30 | public IActionResult GetSimpleSummary(QueryPeopleTasks queryPeopleTasks)
31 | {
32 | return Ok(this._taskReportHandler.GetSimpleSummary(queryPeopleTasks));
33 | }
34 |
35 | [HttpPost]
36 | public IActionResult GetTaskTimeSummaryDetail(QueryPeopleTasks queryPeopleTasks)
37 | {
38 | return Ok(this._taskReportHandler.GetTaskTimeSummaryDetail(queryPeopleTasks));
39 | }
40 |
41 | [HttpPost]
42 | public IActionResult GetTaskTypeSummary(QueryPeopleTasks queryPeopleTasks)
43 | {
44 | return Ok(this._taskReportHandler.GetTaskTypeSummary(queryPeopleTasks));
45 | }
46 |
47 | [HttpPost]
48 | public IActionResult GetTaskSourceSummary(QueryPeopleTasks queryPeopleTasks)
49 | {
50 | return Ok(this._taskReportHandler.GetTaskSourceSummary(queryPeopleTasks));
51 | }
52 |
53 | [HttpPost]
54 | public IActionResult GetTaskTimeSummary(QueryPeopleTasks queryPeopleTasks)
55 | {
56 | return Ok(this._taskReportHandler.GetTaskTimeSummary(queryPeopleTasks));
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/Attributes/SqlDefaultValueAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TimeTracker.DAL.Attributes
4 | {
5 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
6 | public class SqlDefaultValueAttribute : Attribute
7 | {
8 | public string DefaultValue { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/Attributes/UniqueAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TimeTracker.DAL.Attributes
4 | {
5 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
6 | public class UniqueAttribute : Attribute
7 | {
8 | public string DefaultValue { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Auth/MapUserRole.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations.Schema;
2 | using TimeTracker.DAL.DBModels.Auth;
3 | using TimeTracker.DAL.DBModelsBase;
4 |
5 | namespace TimeTracker.DAL.DBModels.Auth
6 | {
7 | public class MapUserRole: EntityDateInfo
8 | {
9 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
10 | public int Id { get; set; }
11 |
12 | public int UserId { get; set; }
13 | public User User { get; set; }
14 |
15 |
16 | public int UserRolesId { get; set; }
17 | public UserRole UserRoles { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Auth/User.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.ComponentModel.DataAnnotations.Schema;
5 | using System.Text.Json.Serialization;
6 | using TimeTracker.DAL.Attributes;
7 | using TimeTracker.DAL.DBModels.Task;
8 | using TimeTracker.DAL.DBModelsBase;
9 | using TimeTracker.DAL.Models;
10 | using TimeTracker.Helper.Auth;
11 |
12 | namespace TimeTracker.DAL.DBModels.Auth
13 | {
14 | public class User: EntityDateInfo
15 | {
16 | [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
17 | public int Id { get; set; }
18 |
19 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
20 | [Unique]
21 | public Guid Guid { get; set; }
22 |
23 | //[Column(TypeName = "NVARCHAR")]
24 | [StringLength(256)]
25 | public string Name { get; set; }
26 |
27 | [Required]
28 | [DataType(DataType.EmailAddress)]
29 | //[Column(TypeName = "NVARCHAR")]
30 | [StringLength(256)]
31 | [Unique]
32 | public string Email { get; set; }
33 | [Required]
34 | public string PasswordHash { get; set; }
35 | [Required]
36 | public AccountStatus AccountStatus { get; set; }
37 |
38 | public ICollection UserRoles { get; set; }
39 | [JsonIgnore]
40 | public List MapUserRoles { get; set; }
41 |
42 | public ICollection TaskDay { get; set; }
43 | //public ICollection Task { get; set; }
44 | //public ICollection TaskTimeRange { get; set; }
45 |
46 | public User()
47 | {
48 |
49 | }
50 |
51 | public User(string email, string password)
52 | {
53 | this.Email = email;
54 | this.PasswordHash = SecurePasswordHasher.Hash(password);
55 | this.AccountStatus = AccountStatus.Uncheck;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Auth/UserImage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.ComponentModel.DataAnnotations.Schema;
5 | using System.Text.Json.Serialization;
6 | using TimeTracker.DAL.Attributes;
7 | using TimeTracker.DAL.DBModels.Task;
8 | using TimeTracker.DAL.DBModelsBase;
9 | using TimeTracker.DAL.Models;
10 | using TimeTracker.Helper.Auth;
11 |
12 | namespace TimeTracker.DAL.DBModels.Auth
13 | {
14 | public class UserImage : EntityDateInfo
15 | {
16 | [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
17 | public Guid Guid { get; set; }
18 |
19 | ///
20 | /// base 64 string
21 | ///
22 | public string Image { get; set; }
23 |
24 | [ForeignKey("User")]
25 | public int UserId { get; set; }
26 |
27 | public User User { get; set; }
28 |
29 | public UserImage(){}
30 |
31 | public UserImage(int userId, string image)
32 | {
33 | this.Guid = Guid.NewGuid();
34 |
35 | this.Image = image;
36 |
37 | this.UserId = userId;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Auth/UserRole.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.ComponentModel.DataAnnotations;
3 | using System.ComponentModel.DataAnnotations.Schema;
4 | using System.Text.Json.Serialization;
5 | using TimeTracker.DAL.Attributes;
6 | using TimeTracker.DAL.DBModelsBase;
7 |
8 | namespace TimeTracker.DAL.DBModels.Auth
9 | {
10 | public class UserRole: EntityDateInfo
11 | {
12 | [Key]
13 | public int Id { get; set; }
14 | ///
15 | /// for matching program code
16 | ///
17 | [Required]
18 | //[Column(TypeName = "NVARCHAR")]
19 | [StringLength(256)]
20 | [Unique]
21 | public string CodeName { get; set; }
22 | //[Column(TypeName = "NVARCHAR")]
23 | [StringLength(256)]
24 | public string DisplayName { get; set; }
25 | [JsonIgnore]
26 | public ICollection Users { get; set; }
27 | [JsonIgnore]
28 | public List MapUserRoles { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Task/DayWorkLimitTime.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 | using System.ComponentModel.DataAnnotations.Schema;
4 | using TimeTracker.DAL.Attributes;
5 | using TimeTracker.DAL.DBModelsBase;
6 |
7 | namespace TimeTracker.DAL.DBModels.Task
8 | {
9 | ///
10 | /// 工作時間上限
11 | ///
12 | public class DayWorkLimitTime: EntityDateInfo
13 | {
14 | [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
15 | public Guid Guid { get; set; }
16 |
17 | ///
18 | /// 工作時間上限
19 | ///
20 | [Required]
21 | [Range(0.0, 24.0)]
22 | public double LimitWorkTime { get; set; } = 7.5;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Task/NonWorkDays.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 | using System.ComponentModel.DataAnnotations.Schema;
4 | using TimeTracker.DAL.Attributes;
5 | using TimeTracker.DAL.DBModelsBase;
6 |
7 | namespace TimeTracker.DAL.DBModels.Task
8 | {
9 | ///
10 | /// 非工作日
11 | ///
12 | public class NonWorkDays: EntityDateInfo
13 | {
14 | [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
15 | public Guid Guid { get; set; }
16 |
17 | ///
18 | /// 非工作日 年月日 year-month-day
19 | ///
20 | [DataType(DataType.Date)]
21 | [Column(TypeName = "Date")]
22 | [Required]
23 | public DateTime NonWorkDate { get; set; }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Task/Period.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 | using System.ComponentModel.DataAnnotations.Schema;
4 | using TimeTracker.DAL.DBModelsBase;
5 |
6 | namespace TimeTracker.DAL.DBModels.Task
7 | {
8 | ///
9 | /// 週期
10 | ///
11 | public class Period: EntityDateInfo
12 | {
13 | [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
14 | public Guid Guid { get; set; }
15 |
16 | ///
17 | /// 年月日 year-month-day
18 | ///
19 | [DataType(DataType.Date)]
20 | [Column(TypeName = "Date")]
21 | [Required]
22 | public DateTime StartDate { get; set; }
23 |
24 | ///
25 | /// 年月日 year-month-day
26 | ///
27 | [DataType(DataType.Date)]
28 | [Column(TypeName = "Date")]
29 | [Required]
30 | public DateTime EndDate { get; set; }
31 |
32 | //[Column(TypeName = "NVARCHAR")]
33 | [StringLength(256)]
34 | public string Name { get; set; }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Task/Task.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 | using TimeTracker.DAL.DBModelsBase;
4 |
5 | namespace TimeTracker.DAL.DBModels.Task
6 | {
7 | ///
8 | /// 一個Task的資訊
9 | ///
10 | public class Task: EntityTask
11 | {
12 | ///
13 | /// 花費時數
14 | ///
15 | [Required]
16 | [Range(0.0, 24.0)]
17 | public double ConsumeTime { get; set; }
18 |
19 | public TaskDay TaskDay { get; set; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Task/TaskDay.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using TimeTracker.DAL.DBModels.Auth;
5 | using TimeTracker.DAL.DBModelsBase;
6 |
7 | namespace TimeTracker.DAL.DBModels.Task
8 | {
9 | public class TaskDay: EntityTaskBase
10 | {
11 | ///
12 | /// 是否請假
13 | ///
14 | [Required]
15 | public bool IsLeave { get; set; }
16 |
17 | public User User { get; set; }
18 |
19 | public ICollection Task { get; set; }
20 |
21 | public ICollection TaskTimeRange { get; set; }
22 |
23 | public TaskDay(){}
24 |
25 | public TaskDay(DateTime date, User user)
26 | {
27 | this.Guid = Guid.NewGuid();
28 | this.Date = date;
29 |
30 | this.IsLeave = false;
31 | this.User = user;
32 | this.Task = null;
33 | this.TaskTimeRange = null;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Task/TaskSource.cs:
--------------------------------------------------------------------------------
1 | using TimeTracker.DAL.DBModelsBase;
2 |
3 | namespace TimeTracker.DAL.DBModels.Task
4 | {
5 | public class TaskSource: EntityOptions
6 | {
7 |
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Task/TaskTimeRange.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 | using TimeTracker.DAL.DBModelsBase;
4 |
5 | namespace TimeTracker.DAL.DBModels.Task
6 | {
7 | public class TaskTimeRange: EntityTask
8 | {
9 | ///
10 | /// 起始時間
11 | ///
12 | [Required]
13 | public DateTime StartTime { get; set; }
14 |
15 | ///
16 | /// 結束時間
17 | ///
18 | [Required]
19 | public DateTime EndTime { get; set; }
20 |
21 | public TaskDay TaskDay { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/Task/TaskType.cs:
--------------------------------------------------------------------------------
1 | using TimeTracker.DAL.DBModelsBase;
2 |
3 | namespace TimeTracker.DAL.DBModels.Task
4 | {
5 | public class TaskType: EntityOptions
6 | {
7 |
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModels/UserRole.cs:
--------------------------------------------------------------------------------
1 | namespace TimeTracker.DAL.DBModels
2 | {
3 | internal class UserRole
4 | {
5 | public int Id { get; set; }
6 | public string CodeName { get; set; }
7 | public string DisplayName { get; set; }
8 | }
9 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModelsBase/EntityDateInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.ComponentModel.DataAnnotations.Schema;
4 | using System.Text.Json.Serialization;
5 | using TimeTracker.DAL.Attributes;
6 |
7 | namespace TimeTracker.DAL.DBModelsBase
8 | {
9 | public class EntityDateInfo
10 | {
11 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
12 | [DefaultValue(typeof(DateTime), "")]
13 | //[SqlDefaultValue(DefaultValue = "getutcdate()")]
14 | [JsonIgnore]
15 | public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
16 |
17 |
18 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
19 | [DefaultValue(typeof(DateTime), "")]
20 | //[SqlDefaultValue(DefaultValue = "getutcdate()")]
21 | [JsonIgnore]
22 | public DateTime UpdatedDate { get; set; } = DateTime.UtcNow;
23 |
24 | public void SetUpdatedDate()
25 | {
26 | this.UpdatedDate = DateTime.UtcNow;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModelsBase/EntityOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 | using System.ComponentModel.DataAnnotations.Schema;
4 | using TimeTracker.DAL.Attributes;
5 |
6 | namespace TimeTracker.DAL.DBModelsBase
7 | {
8 | public class EntityOptions: EntityDateInfo
9 | {
10 | [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
11 | public Guid Guid { get; set; }
12 |
13 | ///
14 | /// for matching program code
15 | ///
16 | [Required]
17 | //[Column(TypeName = "NVARCHAR")]
18 | [StringLength(256)]
19 | [Unique]
20 | public string CodeName { get; set; }
21 |
22 | //[Column(TypeName = "NVARCHAR")]
23 | [StringLength(256)]
24 | public string DisplayName { get; set; }
25 |
26 | public bool IsActive { get; set; } = true;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModelsBase/EntityTask.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 | using TimeTracker.DAL.DBModels.Task;
3 |
4 | namespace TimeTracker.DAL.DBModelsBase
5 | {
6 | public class EntityTask: EntityTaskBase
7 | {
8 | ///
9 | /// 顯示順序
10 | ///
11 | public int DisplayOrder { get; set; }
12 |
13 | ///
14 | /// 工作事項類型
15 | ///
16 | public TaskType TaskType { get; set; }
17 |
18 | ///
19 | /// 工作事項來源
20 | ///
21 | public TaskSource TaskSource { get; set; }
22 |
23 | ///
24 | /// 工作事項名稱
25 | ///
26 | /// [Column(TypeName = "NVARCHAR")]
27 | [StringLength(256)]
28 | public string TaskName { get; set; }
29 |
30 | ///
31 | /// 工作內容
32 | ///
33 | /// [Column(TypeName = "NVARCHAR")]
34 | [StringLength(256)]
35 | public string TaskContent { get; set; }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/DBModelsBase/EntityTaskBase.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.ComponentModel.DataAnnotations.Schema;
5 | using TimeTracker.Helper.Attributes;
6 |
7 | namespace TimeTracker.DAL.DBModelsBase
8 | {
9 | public class EntityTaskBase: EntityDateInfo
10 | {
11 | [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
12 | public Guid Guid { get; set; }
13 |
14 | ///
15 | /// 年月日 year-month-day
16 | ///
17 | [DataType(DataType.Date)]
18 | [Column(TypeName = "Date")]
19 | [Required]
20 | public DateTime Date { get; set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/IUserAuthHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Threading.Tasks;
6 | using TimeTracker.DAL.Models;
7 |
8 | namespace TimeTracker.DAL
9 | {
10 | public interface IUserAuthHandler
11 | where TEntity : class
12 | {
13 | CreateAccountResult Create(TEntity entity);
14 |
15 | void Update(TEntity entity);
16 |
17 | void Delete(TKey id);
18 |
19 | TEntity FindById(TKey id);
20 |
21 | IEnumerable Find(Expression> expression);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/Models/AccountStatus.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace TimeTracker.DAL.Models
7 | {
8 | public enum AccountStatus
9 | {
10 | Uncheck = 0, // 未審核用戶
11 | Approved = 1, // 合法用戶
12 | Rejected = 2, // 拒絕用戶
13 | Suspend = 3, // 停權用戶
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/Models/CreateAccountResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace TimeTracker.DAL.Models
7 | {
8 | public enum CreateAccountResult
9 | {
10 | Ok = 0,
11 | AccountExist = 1,
12 | Fail = 2,
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/Models/UserRoles.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace TimeTracker.DAL.Models
7 | {
8 | public enum UserRoles
9 | {
10 | Admin = 1,
11 | User = 2,
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/DAL/UserAuthHandler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using TimeTracker.DAL.DBModels;
7 | using TimeTracker.DAL.DBModels.Auth;
8 | using TimeTracker.DAL.Models;
9 |
10 | namespace TimeTracker.DAL
11 | {
12 | public class UserAuthHandler : IUserAuthHandler
13 | {
14 | private readonly ApplicationDbContext _context;
15 |
16 | public UserAuthHandler(ApplicationDbContext context)
17 | {
18 | this._context = context;
19 | }
20 |
21 | public CreateAccountResult Create(User entity)
22 | {
23 | var check = _context.User.SingleOrDefault(x => x.Email == entity.Email);
24 | if (check != null)
25 | {
26 | return CreateAccountResult.AccountExist;
27 | }
28 |
29 | _context.User.Add(entity);
30 | _context.SaveChanges();
31 |
32 | _context.MapUserRoles.Add(new MapUserRole()
33 | {
34 | UserId = entity.Id,
35 | UserRolesId = (int)Models.UserRoles.User,
36 | });
37 | _context.SaveChanges();
38 |
39 | return CreateAccountResult.Ok;
40 | }
41 |
42 | public void Delete(int id)
43 | {
44 | var currentUser = FindById(id);
45 | if (currentUser == null)
46 | {
47 | return;
48 | }
49 | _context.User.Remove(currentUser);
50 | _context.SaveChanges();
51 | }
52 |
53 | public void Update(User entity)
54 | {
55 | var currentUser = FindById(entity.Id);
56 | if (currentUser == null)
57 | {
58 | return;
59 | }
60 | entity.SetUpdatedDate();
61 | _context.Entry(currentUser).CurrentValues.SetValues(entity);
62 | _context.SaveChanges();
63 | }
64 |
65 | public IEnumerable Find(Expression> expression)
66 | {
67 | return _context.User.Where(expression);
68 | }
69 |
70 | public User FindById(int id)
71 | {
72 | return _context.User
73 | .Include(x => x.UserRoles)
74 | .SingleOrDefault(x => x.Id == id);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Helper/Attributes/DateFormatConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Text.Json;
4 | using System.Text.Json.Serialization;
5 |
6 | namespace TimeTracker.Helper.Attributes
7 | {
8 | public class DateFormatConverter : JsonConverter
9 | {
10 | public override DateTime Read(
11 | ref Utf8JsonReader reader,
12 | Type typeToConvert,
13 | JsonSerializerOptions options) =>
14 | DateTime.Parse(reader.GetString(), CultureInfo.InvariantCulture);
15 |
16 | public override void Write(
17 | Utf8JsonWriter writer,
18 | DateTime dateTimeValue,
19 | JsonSerializerOptions options) =>
20 | writer.WriteStringValue(dateTimeValue.ToString(
21 | "yyyy-MM-dd", CultureInfo.InvariantCulture));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Helper/Attributes/GuidNotEmptyAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 |
4 | namespace TimeTracker.Helper.Attributes
5 | {
6 | [AttributeUsage(
7 | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
8 | AllowMultiple = false)]
9 | public class GuidNotEmptyAttribute : ValidationAttribute
10 | {
11 | public const string DefaultErrorMessage = "The {0} field must not be empty";
12 | public GuidNotEmptyAttribute() : base(DefaultErrorMessage) { }
13 |
14 | public override bool IsValid(object value)
15 | {
16 | //NotEmpty doesn't necessarily mean required
17 | if (value is null)
18 | {
19 | return true;
20 | }
21 |
22 | switch (value)
23 | {
24 | case Guid guid:
25 | return guid != Guid.Empty;
26 | default:
27 | return true;
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Helper/Attributes/ListNotEmptyAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.ComponentModel.DataAnnotations;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace TimeTracker.Helper.Attributes
9 | {
10 | public class ListNotEmptyAttribute : ValidationAttribute
11 | {
12 | public override bool IsValid(object value)
13 | {
14 | var list = value as IList;
15 | if (list != null)
16 | {
17 | return list.Count > 0;
18 | }
19 | return false;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Helper/Auth/AuthorizeRoleAttribute.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Microsoft.AspNetCore.Mvc.Filters;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using TimeTracker.DAL;
6 | using TimeTracker.DAL.DBModels.Auth;
7 | using TimeTracker.DAL.Models;
8 | using TimeTracker.Helper.Extensions;
9 |
10 | namespace TimeTracker.Helper.Auth
11 | {
12 | public class AuthorizeRoleAttribute : TypeFilterAttribute
13 | {
14 | public AuthorizeRoleAttribute(UserRoles userRole)
15 | : base(typeof(AuthorizeActionFilter))
16 | {
17 | Arguments = new object[] { userRole };
18 | }
19 | }
20 |
21 | public class AuthorizeActionFilter : IAuthorizationFilter
22 | {
23 | private readonly UserRoles _userRole;
24 | protected readonly IUserAuthHandler _authHandler;
25 | public AuthorizeActionFilter(UserRoles userRole, IUserAuthHandler authHandler)
26 | {
27 | this._userRole = userRole;
28 | this._authHandler = authHandler;
29 | }
30 |
31 | public void OnAuthorization(AuthorizationFilterContext context)
32 | {
33 | int? id = context.HttpContext.User.GetUserId();
34 | if (id == null)
35 | {
36 | context.Result = new UnauthorizedResult();
37 | return;
38 | }
39 |
40 | var user = this._authHandler.FindById((int)id);
41 | if (user == null)
42 | {
43 | context.Result = new UnauthorizedResult();
44 | return;
45 | }
46 |
47 | bool isAccountValid = user.AccountStatus == AccountStatus.Approved;
48 | if (!isAccountValid)
49 | {
50 | context.Result = new UnauthorizedResult();
51 | return;
52 | }
53 |
54 | IEnumerable roles = user.UserRoles;
55 | bool isAuthorized = roles.Any(x => x.CodeName.ToLower() == this._userRole.ToString().ToLower() );
56 | if (!isAuthorized)
57 | {
58 | context.Result = new ForbidResult();
59 | return;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Helper/Extensions/DBContextExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using TimeTracker.DAL;
6 |
7 | namespace TimeTracker.Helper.Extensions
8 | {
9 | public static class DBContextExtension
10 | {
11 | // Ref: https://stackoverflow.com/questions/42993860/entity-framework-core-update-many-to-many
12 | public static void TryUpdateManyToMany(this ApplicationDbContext db, IEnumerable currentItems, IEnumerable newItems, Func getKey) where T : class
13 | {
14 | db.Set().RemoveRange(currentItems.Except(newItems, getKey));
15 | db.Set().AddRange(newItems.Except(currentItems, getKey));
16 | }
17 |
18 | public static IEnumerable Except(this IEnumerable items, IEnumerable other, Func getKeyFunc)
19 | {
20 | return items
21 | .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
22 | .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
23 | .Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
24 | .Select(t => t.t.item);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Helper/Extensions/DateTimeExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace TimeTracker.Helper.Extensions
5 | {
6 | public static class DateTimeExtension
7 | {
8 | public static IEnumerable EachDay(this DateTime start, DateTime end)
9 | {
10 | for (var day = start.Date; day.Date <= end.Date; day = day.AddDays(1))
11 | yield return day;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Helper/Extensions/SessionAuthExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Newtonsoft.Json;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Security.Claims;
7 | using System.Threading.Tasks;
8 |
9 | namespace TimeTracker.Helper.Extensions
10 | {
11 | public static class SessionAuthExtensions
12 | {
13 | ///
14 | /// return null if not log in
15 | ///
16 | ///
17 | ///
18 | public static int? GetUserId(this ClaimsPrincipal claimsPrincipal)
19 | {
20 | string idString = claimsPrincipal.FindFirstValue(ClaimTypes.Sid);
21 | int id = -1;
22 |
23 | if (!Int32.TryParse(idString, out id))
24 | {
25 | return null;
26 | }
27 |
28 | return id;
29 | }
30 |
31 | ///
32 | /// return null if not log in
33 | ///
34 | ///
35 | ///
36 | public static Guid? GetUserGuid(this ClaimsPrincipal claimsPrincipal)
37 | {
38 | string idString = claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier);
39 | Guid guid;
40 |
41 | if (Guid.TryParse(idString, out guid))
42 | {
43 | return null;
44 | }
45 |
46 | return guid;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Helper/Extensions/SessionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Newtonsoft.Json;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace TimeTracker.Helper.Extensions
9 | {
10 | public static class SessionExtensions
11 | {
12 | public static void SetObjectAsJson(this ISession session, string key, object value)
13 | {
14 | session.SetString(key, JsonConvert.SerializeObject(value));
15 | }
16 |
17 | public static T GetObjectFromJson(this ISession session, string key)
18 | {
19 | var value = session.GetString(key);
20 | return value == null ? default(T) : JsonConvert.DeserializeObject(value);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Hubs/BaseWSHub.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.SignalR;
2 | using Microsoft.EntityFrameworkCore;
3 | using System;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using TimeTracker.DAL;
7 | using TimeTracker.Helper.Extensions;
8 | using TimeTracker.Hubs.Models;
9 | using TimeTracker.Models.Task;
10 |
11 | namespace TimeTracker.Hubs
12 | {
13 | public class BaseWSHub : Hub
14 | {
15 | protected readonly ApplicationDbContext _context;
16 | protected readonly TaskHandler _taskHandler;
17 | protected readonly WSHubHandler _wsHubHandler;
18 |
19 | public BaseWSHub(ApplicationDbContext context, TaskHandler taskHandler, WSHubHandler wsHubHandler)
20 | {
21 | this._context = context;
22 | this._taskHandler = taskHandler;
23 | this._wsHubHandler = wsHubHandler;
24 | }
25 |
26 | #region Maintaine Group
27 | protected async Task RangeAddToTaskEditorGroup(QueryTasks queryTasks)
28 | {
29 | foreach (DateTime day in queryTasks.StartDate.EachDay(queryTasks.EndDate))
30 | {
31 | await AddToTaskEditorGroup(queryTasks.OwnerGuid, day);
32 | }
33 | }
34 |
35 | protected async Task AddToTaskEditorGroup(Guid guid, DateTime date)
36 | {
37 | await AddToGroup(_wsHubHandler.GetTaskEditorGroupName(guid, date));
38 | }
39 |
40 | protected async Task RangeRemoveFromTaskEditorGroup(QueryTasks queryTasks)
41 | {
42 | foreach (DateTime day in queryTasks.StartDate.EachDay(queryTasks.EndDate))
43 | {
44 | await RemoveFromTaskEditorGroup(queryTasks.OwnerGuid, day);
45 | }
46 | }
47 |
48 | protected async Task RemoveFromTaskEditorGroup(Guid guid, DateTime date)
49 | {
50 | await RemoveFromGroup(_wsHubHandler.GetTaskEditorGroupName(guid, date));
51 | }
52 | #endregion
53 |
54 | #region Basic Methods
55 | protected async Task AddToGroup(string groupName)
56 | {
57 | await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
58 | }
59 | protected async Task RemoveFromGroup(string groupName)
60 | {
61 | await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
62 | }
63 |
64 | protected async Task GroupBroadcast(string groupName, string eventName, object payload)
65 | {
66 | await Clients
67 | //.Groups(groupName, Context.ConnectionId)
68 | .GroupExcept(groupName, Context.ConnectionId)
69 | .SendAsync(eventName, payload);
70 | }
71 | #endregion
72 | }
73 | }
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Hubs/Models/WSMapCode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace TimeTracker.Hubs.Models
7 | {
8 | public enum WSMapCode
9 | {
10 | GetUserInfo = 0,
11 | TaskEditorCreateTask = 1,
12 | TaskEditorDeleteTasks = 2,
13 | TaskEditorUpdateTaskRowOrder = 3,
14 | TaskEditorUpdateTaskCol = 4,
15 | TaskEditorUpdateIsLeave = 5,
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Hubs/UserIdProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.SignalR;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Security.Claims;
6 | using System.Threading.Tasks;
7 |
8 | namespace TimeTracker.Hubs
9 | {
10 | public class UserIdProvider: IUserIdProvider
11 | {
12 | public virtual string GetUserId(HubConnectionContext connection)
13 | {
14 | return connection.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Hubs/WSHubHandler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.SignalR;
2 | using System;
3 | using System.Threading.Tasks;
4 | using TimeTracker.Hubs.Models;
5 | using TimeTracker.DAL;
6 |
7 | namespace TimeTracker.Hubs
8 | {
9 | public class WSHubHandler where THub : Hub
10 | {
11 | protected readonly ApplicationDbContext _context;
12 | protected readonly IHubContext _hubContext;
13 |
14 | public WSHubHandler(ApplicationDbContext context, IHubContext hubContext)
15 | {
16 | this._context = context;
17 | this._hubContext = hubContext;
18 | }
19 |
20 | ///
21 | /// ask specific user to get user info by guid
22 | ///
23 | ///
24 | ///
25 | public Task ClientGetUserInfo(Guid guid)
26 | {
27 | return this._hubContext.Clients.User(guid.ToString()).SendAsync(WSMapCode.GetUserInfo.ToString(), guid.ToString());
28 | }
29 |
30 | public string GetTaskEditorGroupName(Guid ownerGuid, DateTime date)
31 | {
32 | return GetTaskEditorGroupName(ownerGuid, date.ToString("yyyy-MM-dd"));
33 | }
34 |
35 | public string GetTaskEditorGroupName(Guid ownerGuid, string date)
36 | {
37 | return $"TaskEditor+{ownerGuid.ToString()}+{date}";
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using TimeTracker.DAL.DBModels.Auth;
3 | using TimeTracker.Models;
4 |
5 | namespace TimeTracker
6 | {
7 | public class MappingProfile : Profile
8 | {
9 | public MappingProfile()
10 | {
11 | // Add as many of these lines as you need to map your objects
12 | CreateMap();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/ApprovedUser.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using System;
3 | using TimeTracker.DAL.DBModels.Auth;
4 |
5 | namespace TimeTracker.Models
6 | {
7 | public class ApprovedUser
8 | {
9 | public Guid Guid { get; set; }
10 |
11 | public string Name { get; set; }
12 |
13 | public string Email { get; set; }
14 |
15 | public ApprovedUser(User user)
16 | {
17 | var mapperConfig = new MapperConfiguration(cfg =>
18 | {
19 | cfg.CreateMap();
20 | });
21 | IMapper mapper = mapperConfig.CreateMapper();
22 | mapper.Map(user, this);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/AuthenticationUser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace TimeTracker.Models
8 | {
9 | public class AuthenticationUser
10 | {
11 | [Required(AllowEmptyStrings = false)]
12 | [EmailAddress]
13 | public string Email { get; set; }
14 |
15 | [Required(AllowEmptyStrings = false)]
16 | public string Password { get; set; }
17 |
18 | public string Name { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/Period/QueryPeriod.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Text.Json.Serialization;
6 | using System.Threading.Tasks;
7 | using TimeTracker.Helper.Attributes;
8 |
9 | namespace TimeTracker.Models.Period
10 | {
11 | public class QueryPeriod
12 | {
13 | [GuidNotEmpty]
14 | public Guid Guid { get; set; }
15 |
16 | [JsonConverter(typeof(DateFormatConverter))]
17 | public DateTime StartDate { get; set; }
18 |
19 | [JsonConverter(typeof(DateFormatConverter))]
20 | public DateTime EndDate { get; set; }
21 |
22 | [StringLength(256)]
23 | public string Name { get; set; }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/RegistUser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace TimeTracker.Models
8 | {
9 | public class RegistUser
10 | {
11 | [Required(AllowEmptyStrings = false)]
12 | [EmailAddress]
13 | public string Email { get; set; }
14 |
15 | [Required(AllowEmptyStrings = false)]
16 | public string Password { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/SessionUserData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace TimeTracker.Models
7 | {
8 | public class SessionUserData
9 | {
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/Task/CreateTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace TimeTracker.Models.Task
5 | {
6 | public class CreateTask
7 | {
8 | public List Tasks { get; set; }
9 |
10 | public Guid OwnerGuid { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/Task/DeleteTasks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace TimeTracker.Models.Task
7 | {
8 | public class DeleteTasks
9 | {
10 | public Guid OwnerGuid { get; set; }
11 |
12 | ///
13 | /// yyy-MM-dd
14 | ///
15 | public string Date { get; set; }
16 |
17 | public List TaskGuids { get; set; }
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/Task/GetDaysDataResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text.Json.Serialization;
4 | using TimeTracker.Helper.Attributes;
5 |
6 | namespace TimeTracker.Models.Task
7 | {
8 | public class GetDaysDataResponse
9 | {
10 | public Guid Guid { get; set; }
11 |
12 | [JsonConverter(typeof(DateFormatConverter))]
13 | public DateTime Date { get; set; }
14 |
15 | public bool IsLeave { get; set; }
16 |
17 | public List FormData { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/Task/PersonSummary.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text.Json.Serialization;
5 | using System.Threading.Tasks;
6 | using TimeTracker.Helper.Attributes;
7 |
8 | namespace TimeTracker.Models.Task
9 | {
10 | public class PersonSummary
11 | {
12 | [JsonConverter(typeof(DateFormatConverter))]
13 | public DateTime Date { get; set; }
14 |
15 | public double ConsumeTime
16 | {
17 | get{
18 | return this.IsLeave ? this._LimitWorkTime : this._ConsumeTime;
19 | }
20 | }
21 |
22 | public double Overtime
23 | {
24 | get
25 | {
26 | return this.IsLeave ? 0 :
27 | this.ConsumeTime - this._LimitWorkTime > 0 ? this.ConsumeTime - this._LimitWorkTime : 0;
28 | }
29 | }
30 |
31 | public bool IsLeave { get; set; } = false;
32 |
33 | protected double _ConsumeTime { get; set; }
34 |
35 | protected double _LimitWorkTime { get; set; }
36 |
37 | public PersonSummary(){}
38 |
39 | public PersonSummary(DateTime date, double consumeTime, double limitWorkTime, bool isLeave)
40 | {
41 | this.Date = date;
42 | this._ConsumeTime = consumeTime;
43 | this._LimitWorkTime = limitWorkTime;
44 | this.IsLeave = isLeave;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/Task/PieRow.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace TimeTracker.Models.Task
7 | {
8 | public class PieRow
9 | {
10 | public string Name { get; set; }
11 | public double Value { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/Task/QueryPeopleTasks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Text.Json.Serialization;
6 | using System.Threading.Tasks;
7 | using TimeTracker.Helper.Attributes;
8 |
9 | namespace TimeTracker.Models.Task
10 | {
11 | public class QueryPeopleTasks
12 | {
13 | [ListNotEmpty(ErrorMessage ="At least a person is required")]
14 | public List OwnerGuids { get; set; }
15 |
16 | [Required]
17 | [JsonConverter(typeof(DateFormatConverter))]
18 | public DateTime StartDate { get; set; }
19 |
20 | [Required]
21 | [JsonConverter(typeof(DateFormatConverter))]
22 | public DateTime EndDate { get; set; }
23 |
24 | public QueryPeopleTasks(){}
25 |
26 | public QueryPeopleTasks(QueryTasks queryTasks)
27 | {
28 | this.OwnerGuids = (new[] { queryTasks.OwnerGuid }).ToList();
29 | this.StartDate = queryTasks.StartDate;
30 | this.EndDate = queryTasks.EndDate;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/Task/QueryTasks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Text.Json.Serialization;
6 | using System.Threading.Tasks;
7 | using TimeTracker.Helper.Attributes;
8 |
9 | namespace TimeTracker.Models.Task
10 | {
11 | public class QueryTasks
12 | {
13 | [GuidNotEmpty]
14 | public Guid OwnerGuid { get; set; }
15 |
16 | [Required]
17 | [JsonConverter(typeof(DateFormatConverter))]
18 | public DateTime StartDate { get; set; }
19 |
20 | [Required]
21 | [JsonConverter(typeof(DateFormatConverter))]
22 | public DateTime EndDate { get; set; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/Task/SimpleSummary.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text.Json.Serialization;
5 | using System.Threading.Tasks;
6 | using TimeTracker.Helper.Attributes;
7 |
8 | namespace TimeTracker.Models.Task
9 | {
10 | public class SimpleSummary
11 | {
12 | [JsonConverter(typeof(DateFormatConverter))]
13 | public DateTime Date { get; set; }
14 |
15 | public double ConsumeTime { get; set; }
16 |
17 | public double Overtime { get; set; }
18 |
19 | public bool IsLeave { get; set; } = false;
20 |
21 | public SimpleSummary(){}
22 |
23 | public SimpleSummary(MultiSimpleSummary multiSimpleSummary)
24 | {
25 | this.Date = multiSimpleSummary.Date;
26 | this.ConsumeTime = multiSimpleSummary.TotalConsumeTime;
27 | this.Overtime = multiSimpleSummary.TotalOvertime;
28 | }
29 |
30 | public SimpleSummary(PersonSummary personSummary)
31 | {
32 | this.Date = personSummary.Date;
33 | this.ConsumeTime = personSummary.ConsumeTime;
34 | this.Overtime = personSummary.Overtime;
35 | this.IsLeave = personSummary.IsLeave;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/Task/UpdateIsLeave.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text.Json.Serialization;
5 | using System.Threading.Tasks;
6 | using TimeTracker.Helper.Attributes;
7 |
8 | namespace TimeTracker.Models.Task
9 | {
10 | public class UpdateIsLeave
11 | {
12 | public Guid OwnerGuid { get; set; }
13 |
14 | [JsonConverter(typeof(DateFormatConverter))]
15 | public DateTime Date { get; set; }
16 |
17 | public bool IsLeave { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/Task/UpdateTaskRowOrder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text.Json.Serialization;
5 | using System.Threading.Tasks;
6 | using TimeTracker.Helper.Attributes;
7 |
8 | namespace TimeTracker.Models.Task
9 | {
10 | public class UpdateTaskRowOrder
11 | {
12 | public Guid OwnerGuid { get; set; }
13 |
14 | ///
15 | /// Task guid
16 | ///
17 | public Guid Guid { get; set; }
18 |
19 | [JsonConverter(typeof(DateFormatConverter))]
20 | public DateTime Date { get; set; }
21 |
22 | public int DisplayOrder { get; set; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/UpdateAccount.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using TimeTracker.DAL.DBModels;
7 | using TimeTracker.DAL.Models;
8 |
9 | namespace TimeTracker.Models
10 | {
11 | public class UpdateAccount
12 | {
13 | [Required]
14 | public Guid Guid { get; set; }
15 |
16 | public string Name { get; set; }
17 | public bool IsUpdateName { get; set; }
18 |
19 | public AccountStatus AccountStatus { get; set; }
20 | public bool IsUpdateAccountStatus { get; set; }
21 |
22 | public ICollection UserRoles { get; set; }
23 | public bool IsUpdateUserRoles { get; set; }
24 |
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/UpdatePassword.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace TimeTracker.Models
7 | {
8 | public class UpdatePassword
9 | {
10 | public string CurrentPassword { get; set; }
11 | public string Password { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/UpdateUser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace TimeTracker.Models
8 | {
9 | public class UpdateUser
10 | {
11 | public string Name { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Models/UserInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using TimeTracker.DAL.DBModels.Auth;
4 | using TimeTracker.DAL.Models;
5 |
6 | namespace TimeTracker.Models
7 | {
8 | public class UserInfo
9 | {
10 | public Guid Guid { get; set; }
11 | public string Name { get; set; }
12 | public string Email { get; set; }
13 |
14 | public AccountStatus AccountStatus { get; set; }
15 |
16 | public ICollection UserRoles { get; set; }
17 |
18 | public UserInfo(User user)
19 | {
20 | this.Guid = user.Guid;
21 | this.Name = user.Name;
22 | this.Email = user.Email;
23 | this.AccountStatus = user.AccountStatus;
24 | this.UserRoles = user.UserRoles;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Pages/Error.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ErrorModel
3 | @{
4 | ViewData["Title"] = "Error";
5 | }
6 |
7 | Error.
8 | An error occurred while processing your request.
9 |
10 | @if (Model.ShowRequestId)
11 | {
12 |
13 | Request ID: @Model.RequestId
14 |
15 | }
16 |
17 | Development Mode
18 |
19 | Swapping to the Development environment displays detailed information about the error that occurred.
20 |
21 |
22 | The Development environment shouldn't be enabled for deployed applications.
23 | It can result in displaying sensitive information from exceptions to end users.
24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
25 | and restarting the app.
26 |
27 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Pages/Error.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.AspNetCore.Mvc.RazorPages;
8 | using Microsoft.Extensions.Logging;
9 |
10 | namespace TimeTracker.Pages
11 | {
12 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
13 | public class ErrorModel : PageModel
14 | {
15 | private readonly ILogger logger;
16 |
17 | public ErrorModel(ILogger _logger)
18 | {
19 | logger = _logger;
20 | }
21 | public string RequestId { get; set; }
22 |
23 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
24 |
25 | public void OnGet()
26 | {
27 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Pages/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using TimeTracker
2 | @namespace TimeTracker.Pages
3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
4 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.Hosting;
8 | using Microsoft.Extensions.Logging;
9 |
10 | namespace TimeTracker
11 | {
12 | public class Program
13 | {
14 | public static void Main(string[] args)
15 | {
16 | CreateHostBuilder(args).Build().Run();
17 | }
18 |
19 | public static IHostBuilder CreateHostBuilder(string[] args) =>
20 | Host.CreateDefaultBuilder(args)
21 | .ConfigureWebHostDefaults(webBuilder =>
22 | {
23 | webBuilder.UseStartup();
24 | });
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:62213",
7 | "sslPort": 44328
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "TimeTracker": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "applicationUrl": "https://localhost:5001;http://localhost:5000",
22 | "environmentVariables": {
23 | "ASPNETCORE_ENVIRONMENT": "Development"
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Debug",
6 | "Microsoft": "Debug",
7 | "Microsoft.Hosting.Lifetime": "Debug"
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/TimeTracker/TimeTracker/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "IsLaunchSPAServer": false,
11 | "DBType": "MS", // MS, PG
12 | "ConnectionStrings": {
13 | "MS": "Server=DESKTOP-3IQ61RH\\SQLEXPRESS;Database=TestTimeTracker;Integrated Security=True;",
14 | "PG": "User ID=postgres;Password=myPWD;Host=localhost;Port=5432;Database=TestTimeTracker;Pooling=true;Integrated Security=true;"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/release_windows/CreateDB_CreateTable_CreateInitData.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/release_windows/CreateDB_CreateTable_CreateInitData.sql
--------------------------------------------------------------------------------
/release_windows/publish.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/release_windows/publish.zip
--------------------------------------------------------------------------------
/screen_shot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jchou24/TimeTracker/a87fb634c92e4607e44dc1528c0d3f0f3f6b362b/screen_shot.png
--------------------------------------------------------------------------------