Initial commit
This commit is contained in:
15
Tasker.Ui/.eslintrc.cjs
Normal file
15
Tasker.Ui/.eslintrc.cjs
Normal file
@@ -0,0 +1,15 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
'@vue/eslint-config-prettier/skip-formatting'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
||||
30
Tasker.Ui/.gitignore
vendored
Normal file
30
Tasker.Ui/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
8
Tasker.Ui/.prettierrc.json
Normal file
8
Tasker.Ui/.prettierrc.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
7
Tasker.Ui/.vscode/extensions.json
vendored
Normal file
7
Tasker.Ui/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
45
Tasker.Ui/README.md
Normal file
45
Tasker.Ui/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Tasker.Ui
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Run Unit Tests with [Vitest](https://vitest.dev/)
|
||||
|
||||
```sh
|
||||
npm run test:unit
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
||||
1
Tasker.Ui/env.d.ts
vendored
Normal file
1
Tasker.Ui/env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
16
Tasker.Ui/index.html
Normal file
16
Tasker.Ui/index.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Welcome to Vuetify 3</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
6804
Tasker.Ui/package-lock.json
generated
Normal file
6804
Tasker.Ui/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
50
Tasker.Ui/package.json
Normal file
50
Tasker.Ui/package.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "tasker-ui",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": "vite preview",
|
||||
"test:unit": "vitest",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --build --force",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "^7.4.47",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"axios": "^1.6.7",
|
||||
"pinia": "^2.1.7",
|
||||
"uuidv7": "^1.0.1",
|
||||
"vue": "^3.4.29",
|
||||
"vue-router": "^4.3.3",
|
||||
"vuetify": "^3.5.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.8.0",
|
||||
"@tsconfig/node20": "^20.1.4",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"@types/node": "^20.14.5",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"@vue/eslint-config-typescript": "^13.0.0",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"jsdom": "^24.1.0",
|
||||
"npm-run-all2": "^6.2.0",
|
||||
"postcss": "^8.4.41",
|
||||
"prettier": "^3.2.5",
|
||||
"tailwindcss": "^3.4.9",
|
||||
"typescript": "~5.4.0",
|
||||
"vite": "^5.3.1",
|
||||
"vite-plugin-vue-devtools": "^7.3.1",
|
||||
"vitest": "^1.6.0",
|
||||
"vue-tsc": "^2.0.21"
|
||||
}
|
||||
}
|
||||
6
Tasker.Ui/postcss.config.js
Normal file
6
Tasker.Ui/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
BIN
Tasker.Ui/public/favicon.ico
Normal file
BIN
Tasker.Ui/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
100
Tasker.Ui/src/App.vue
Normal file
100
Tasker.Ui/src/App.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<v-layout>
|
||||
|
||||
<v-system-bar app
|
||||
color="secondary">
|
||||
</v-system-bar>
|
||||
|
||||
<v-navigation-drawer
|
||||
app
|
||||
v-model="drawer"
|
||||
:rail="rail"
|
||||
permanent
|
||||
@click="rail = !rail"
|
||||
>
|
||||
<v-list-item
|
||||
prepend-avatar="https://randomuser.me/api/portraits/men/85.jpg"
|
||||
title="John Leider"
|
||||
>
|
||||
|
||||
</v-list-item>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-list density="compact" nav>
|
||||
<v-list-item prepend-icon="mdi-home-city"
|
||||
title="Inbox"
|
||||
value="home"
|
||||
></v-list-item>
|
||||
<v-list-item prepend-icon="mdi-account"
|
||||
title="Today"
|
||||
value="account"
|
||||
></v-list-item>
|
||||
<v-list-item prepend-icon="mdi-account-group-outline"
|
||||
title="Upcoming"
|
||||
value="users"
|
||||
></v-list-item>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-app-bar app
|
||||
color="primary"
|
||||
prominent>
|
||||
|
||||
<v-app-bar-nav-icon variant="text"
|
||||
@click.stop="drawer = !drawer">
|
||||
</v-app-bar-nav-icon>
|
||||
|
||||
<v-toolbar-title>
|
||||
My files
|
||||
</v-toolbar-title>
|
||||
|
||||
</v-app-bar>
|
||||
|
||||
<v-main>
|
||||
<v-container fluid>
|
||||
<router-view></router-view>
|
||||
</v-container>
|
||||
</v-main>
|
||||
|
||||
<v-footer app class="flex bg-amber font-mono justify-center">
|
||||
©2024 Tasker
|
||||
</v-footer>
|
||||
|
||||
</v-layout>
|
||||
|
||||
</v-app>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const drawer = ref(true)
|
||||
const rail = ref(false)
|
||||
const group = ref(null)
|
||||
const items = ref([
|
||||
{
|
||||
title: 'Foo',
|
||||
value: 'foo'
|
||||
},
|
||||
{
|
||||
title: 'Bar',
|
||||
value: 'bar'
|
||||
},
|
||||
{
|
||||
title: 'Fizz',
|
||||
value: 'fizz'
|
||||
},
|
||||
{
|
||||
title: 'Buzz',
|
||||
value: 'buzz'
|
||||
}
|
||||
])
|
||||
|
||||
watch(group, () => {
|
||||
drawer.value = false
|
||||
})
|
||||
|
||||
</script>
|
||||
120
Tasker.Ui/src/api/taskApi.ts
Normal file
120
Tasker.Ui/src/api/taskApi.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
export interface Task {
|
||||
id: string;
|
||||
name: string; // [MaxLength(255)]
|
||||
description?: string | null; // [MaxLength(2048)]
|
||||
dueDate?: string | null;
|
||||
}
|
||||
|
||||
export interface TaskApiClient {
|
||||
getTasks(): Promise<Task[]>;
|
||||
|
||||
addTask(task: Task): Promise<Task>;
|
||||
}
|
||||
|
||||
export class TaskApiClientImpl implements TaskApiClient {
|
||||
private readonly baseUrl: string
|
||||
|
||||
constructor(baseUrl: string) {
|
||||
this.baseUrl = baseUrl
|
||||
}
|
||||
|
||||
async getTasks(): Promise<Task[]> {
|
||||
const response = await fetch(`${this.baseUrl}/tasks`)
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch tasks')
|
||||
}
|
||||
const data = await response.json()
|
||||
return data as Task[]
|
||||
}
|
||||
|
||||
async addTask(task: Task): Promise<Task> {
|
||||
const response = await fetch(`${this.baseUrl}/tasks`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(task)
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to add task')
|
||||
}
|
||||
const data = await response.json()
|
||||
return data as Task
|
||||
}
|
||||
|
||||
async completeTask(id: string): Promise<Task> {
|
||||
const response = await fetch(`${this.baseUrl}/tasks/${id}/complete`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
'CompleteOn': new Date().toISOString()
|
||||
})
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to add task')
|
||||
}
|
||||
const data = await response.json()
|
||||
return data as Task
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function useTasks() {
|
||||
const tasks = ref<Task[]>([])
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
const taskApiClient = new TaskApiClientImpl('https://localhost:7055')
|
||||
|
||||
const fetchTasks = async () => {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
tasks.value = await taskApiClient.getTasks()
|
||||
} catch (err) {
|
||||
error.value = (err as Error).message
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const addTask = async (task: Task) => {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const newTask = await taskApiClient.addTask(task)
|
||||
tasks.value.push(newTask)
|
||||
} catch (err) {
|
||||
error.value = (err as Error).message
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const completeTask = async (id: string) => {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
await taskApiClient.completeTask(id)
|
||||
} catch (err) {
|
||||
error.value = (err as Error).message
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(fetchTasks)
|
||||
|
||||
return {
|
||||
tasks,
|
||||
isLoading,
|
||||
error,
|
||||
fetchTasks,
|
||||
addTask,
|
||||
completeTask
|
||||
}
|
||||
}
|
||||
86
Tasker.Ui/src/assets/base.css
Normal file
86
Tasker.Ui/src/assets/base.css
Normal file
@@ -0,0 +1,86 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
1
Tasker.Ui/src/assets/logo.svg
Normal file
1
Tasker.Ui/src/assets/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
35
Tasker.Ui/src/assets/main.css
Normal file
35
Tasker.Ui/src/assets/main.css
Normal file
@@ -0,0 +1,35 @@
|
||||
/*@import './base.css';*/
|
||||
|
||||
/*#app {*/
|
||||
/* max-width: 1280px;*/
|
||||
/* margin: 0 auto;*/
|
||||
/* padding: 2rem;*/
|
||||
/* font-weight: normal;*/
|
||||
/*}*/
|
||||
|
||||
/*a,*/
|
||||
/*.green {*/
|
||||
/* text-decoration: none;*/
|
||||
/* color: hsla(160, 100%, 37%, 1);*/
|
||||
/* transition: 0.4s;*/
|
||||
/* padding: 3px;*/
|
||||
/*}*/
|
||||
|
||||
/*@media (hover: hover) {*/
|
||||
/* a:hover {*/
|
||||
/* background-color: hsla(160, 100%, 37%, 0.2);*/
|
||||
/* }*/
|
||||
/*}*/
|
||||
|
||||
/*@media (min-width: 1024px) {*/
|
||||
/* body {*/
|
||||
/* display: flex;*/
|
||||
/* place-items: center;*/
|
||||
/* }*/
|
||||
|
||||
/* #app {*/
|
||||
/* display: grid;*/
|
||||
/* grid-template-columns: 1fr 1fr;*/
|
||||
/* padding: 0 2rem;*/
|
||||
/* }*/
|
||||
/*}*/
|
||||
72
Tasker.Ui/src/components/TaskInput.vue
Normal file
72
Tasker.Ui/src/components/TaskInput.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { type Task, useTasks } from '@/api/taskApi'
|
||||
import { uuidv7 } from 'uuidv7'
|
||||
|
||||
const name = ref('')
|
||||
const description = ref(null)
|
||||
const dueDate = ref(null)
|
||||
|
||||
const { addTask } = useTasks()
|
||||
|
||||
const doAddTask = () => {
|
||||
const newTask: Task = {
|
||||
id: uuidv7(),
|
||||
name: name.value,
|
||||
description: description.value,
|
||||
dueDate: dueDate.value
|
||||
}
|
||||
addTask(newTask)
|
||||
}
|
||||
|
||||
const cancelAddTask = () => {
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<v-text-field label="Task name"
|
||||
v-model="name"
|
||||
variant="underlined">
|
||||
</v-text-field>
|
||||
|
||||
<v-text-field label="Description"
|
||||
v-model="description"
|
||||
variant="underlined">
|
||||
</v-text-field>
|
||||
|
||||
<div class="flex flex-row space-x-2">
|
||||
|
||||
<span>due-date</span>
|
||||
|
||||
<span>priority</span>
|
||||
<span>tags</span>
|
||||
<span>reminders</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class=" bg-teal-400 p-2 flex flex-row space-x-2 justify-end">
|
||||
|
||||
<span class="justify-start">projects</span>
|
||||
|
||||
<div class="bg-red space-x-2 justify-end">
|
||||
<v-btn @click="cancelAddTask()"
|
||||
variant="outlined">
|
||||
Cancel
|
||||
</v-btn>
|
||||
|
||||
<v-btn @click="doAddTask()"
|
||||
color="primary"
|
||||
variant="flat">
|
||||
Add Task
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
44
Tasker.Ui/src/components/TaskList.vue
Normal file
44
Tasker.Ui/src/components/TaskList.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import { useTasks } from '@/api/taskApi'
|
||||
|
||||
const { completeTask, tasks, isLoading, error } = useTasks()
|
||||
|
||||
function itemCompleted(id: string) {
|
||||
console.log(`completing task: ${id}`)
|
||||
completeTask(id)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-list v-if="!isLoading && !error">
|
||||
<v-list-item v-for="task in tasks" :key="task.id">
|
||||
<div class="flex p-1 bg-amber">
|
||||
|
||||
<div>
|
||||
<v-checkbox-btn @change="itemCompleted(task.id)"
|
||||
></v-checkbox-btn>
|
||||
</div>
|
||||
|
||||
<div class="flex-column">
|
||||
<div class="">
|
||||
<div class="font-sans">{{ task.name }}</div>
|
||||
<div class="font-serif font-normal">Description {{ task.description }}</div>
|
||||
</div>
|
||||
|
||||
<div>{{ task.dueDate }}</div>
|
||||
<!-- <div>{{ task.project.Name }}</div>-->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-alert v-else-if="error" type="error">
|
||||
{{ error }}
|
||||
</v-alert>
|
||||
<v-progress-circular v-else indeterminate></v-progress-circular>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
4
Tasker.Ui/src/index.css
Normal file
4
Tasker.Ui/src/index.css
Normal file
@@ -0,0 +1,4 @@
|
||||
/* ./src/index.css */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
18
Tasker.Ui/src/main.ts
Normal file
18
Tasker.Ui/src/main.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import vuetify from './vuetify'
|
||||
import 'vuetify/styles' // Ensure this line is present
|
||||
import '@mdi/font/css/materialdesignicons.css' // Import the Material Design Icons CSS
|
||||
import './assets/main.css'
|
||||
import './index.css'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(vuetify)
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
23
Tasker.Ui/src/router/index.ts
Normal file
23
Tasker.Ui/src/router/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (About.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () => import('../views/AboutView.vue')
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
||||
12
Tasker.Ui/src/stores/counter.ts
Normal file
12
Tasker.Ui/src/stores/counter.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {ref, computed} from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment }
|
||||
})
|
||||
16
Tasker.Ui/src/views/AboutView.vue
Normal file
16
Tasker.Ui/src/views/AboutView.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
<p class="bg-red-400">Let's try!</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
14
Tasker.Ui/src/views/HomeView.vue
Normal file
14
Tasker.Ui/src/views/HomeView.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import {useCounterStore} from "@/stores/counter";
|
||||
import TaskInput from "@/components/TaskInput.vue";
|
||||
import TaskList from '@/components/TaskList.vue'
|
||||
|
||||
const counterStore = useCounterStore()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<task-input></task-input>
|
||||
<task-list></task-list>
|
||||
|
||||
</template>
|
||||
20
Tasker.Ui/src/vuetify.ts
Normal file
20
Tasker.Ui/src/vuetify.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'vuetify/styles'
|
||||
import { createVuetify } from 'vuetify'
|
||||
import { aliases, mdi } from 'vuetify/iconsets/mdi'
|
||||
|
||||
import * as components from 'vuetify/components'
|
||||
import * as directives from 'vuetify/directives'
|
||||
|
||||
const vuetify = createVuetify({
|
||||
components,
|
||||
directives,
|
||||
icons: {
|
||||
defaultSet: 'mdi',
|
||||
aliases,
|
||||
sets: {
|
||||
mdi,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default vuetify
|
||||
12
Tasker.Ui/tailwind.config.js
Normal file
12
Tasker.Ui/tailwind.config.js
Normal file
@@ -0,0 +1,12 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
'./public/**/*.html',
|
||||
'./src/**/*.{js,jsx,ts,tsx,vue}',
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
14
Tasker.Ui/tsconfig.app.json
Normal file
14
Tasker.Ui/tsconfig.app.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Tasker.Ui/tsconfig.json
Normal file
14
Tasker.Ui/tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.vitest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
19
Tasker.Ui/tsconfig.node.json
Normal file
19
Tasker.Ui/tsconfig.node.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "@tsconfig/node20/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
||||
11
Tasker.Ui/tsconfig.vitest.json
Normal file
11
Tasker.Ui/tsconfig.vitest.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
"exclude": [],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo",
|
||||
|
||||
"lib": [],
|
||||
"types": ["node", "jsdom"]
|
||||
}
|
||||
}
|
||||
18
Tasker.Ui/vite.config.ts
Normal file
18
Tasker.Ui/vite.config.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
vueDevTools(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
})
|
||||
14
Tasker.Ui/vitest.config.ts
Normal file
14
Tasker.Ui/vitest.config.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
|
||||
import viteConfig from './vite.config'
|
||||
|
||||
export default mergeConfig(
|
||||
viteConfig,
|
||||
defineConfig({
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
exclude: [...configDefaults.exclude, 'e2e/**'],
|
||||
root: fileURLToPath(new URL('./', import.meta.url))
|
||||
}
|
||||
})
|
||||
)
|
||||
Reference in New Issue
Block a user