generated from alphane/template
Compare commits
15 Commits
8fe16ef65c
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f7e474a011 | |||
| 6d211a1e83 | |||
| 7b7330d9b4 | |||
| 19352c7fa6 | |||
| ec6b9d2403 | |||
| e1664bf724 | |||
| d581c7b789 | |||
| 8457a97c5f | |||
| 4b91ba0936 | |||
| 8030e98d90 | |||
| 4c9e503e4f | |||
| cb60ec8480 | |||
| f83d4d579c | |||
| 033c962ebe | |||
| 69d4f7c3f0 |
@@ -0,0 +1,16 @@
|
|||||||
|
HOST_PORT=8080
|
||||||
|
API_PORT=8080
|
||||||
|
|
||||||
|
DB_HOST=db
|
||||||
|
DB_USERNAME=calebburkedev
|
||||||
|
DB_DATABASE=calebburkedev_production
|
||||||
|
DB_PASSWORD=DevPwd99!
|
||||||
|
DB_PORT=5432
|
||||||
|
|
||||||
|
DB_TRUST_SERVER_CERTIFICATE=true
|
||||||
|
|
||||||
|
VITE_APPLICATION_NAME="CALEB BURKE DEV"
|
||||||
|
VITE_API_BASE_URL="http://localhost:8080"
|
||||||
|
VITE_AUTH0_CLIENT_ID="TRlKzdNBynpo9tU1RSmnF0p8d3IEam4J"
|
||||||
|
VITE_AUTH0_AUDIENCE="alphane-api"
|
||||||
|
VITE_AUTH0_DOMAIN="https=//dev-7mdjzcgwirhocfwm.ca.auth0.com"
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Build and Push Docker Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Log in to Gitea Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ vars.REGISTRY_URL }}
|
||||||
|
username: ${{ secrets.REGISTRY_USER }}
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ vars.REGISTRY_URL }}/${{ github.repository }}:latest
|
||||||
|
${{ vars.REGISTRY_URL }}/${{ github.repository }}:${{ github.sha }}
|
||||||
|
labels: |
|
||||||
|
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
||||||
|
build-args: |
|
||||||
|
RELEASE_TAG=${{ github.ref_name }}
|
||||||
|
GIT_COMMIT_HASH=${{ github.sha }}
|
||||||
@@ -1,5 +1,60 @@
|
|||||||
# calebburke.dev
|
# calebburke.dev
|
||||||
|
|
||||||
|
## Deploying
|
||||||
|
|
||||||
|
### Production Environment (remote)
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
### Test Production Build Locally
|
||||||
|
|
||||||
|
Files:
|
||||||
|
|
||||||
|
- [Dockerfile](./Dockerfile)
|
||||||
|
- [docker-compose.yml](./docker-compose.yml)
|
||||||
|
- Non-commited `.env` file
|
||||||
|
|
||||||
|
1. Create a `.env` file in top level directory with the appropriate values.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
HOST_PORT=8080
|
||||||
|
API_PORT=8080
|
||||||
|
|
||||||
|
DB_HOST=db
|
||||||
|
DB_USERNAME=calebburkedev
|
||||||
|
DB_DATABASE=calebburkedev_production
|
||||||
|
DB_PASSWORD=DevPwd99!
|
||||||
|
DB_PORT=5432
|
||||||
|
|
||||||
|
DB_TRUST_SERVER_CERTIFICATE=true
|
||||||
|
|
||||||
|
VITE_APPLICATION_NAME="CALEB BURKE DEV"
|
||||||
|
VITE_API_BASE_URL="http://localhost:8080"
|
||||||
|
VITE_AUTH0_CLIENT_ID="TRlKzdNBynpo9tU1RSmnF0p8d3IEam4J"
|
||||||
|
VITE_AUTH0_AUDIENCE="alphane-api"
|
||||||
|
VITE_AUTH0_DOMAIN="https=//dev-7mdjzcgwirhocfwm.ca.auth0.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. (optional) If testing build arguments do
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose build \
|
||||||
|
--build-arg RELEASE_TAG=$(date +%Y.%m.%d) \
|
||||||
|
--build-arg GIT_COMMIT_HASH=$(git rev-parse HEAD)
|
||||||
|
```
|
||||||
|
|
||||||
|
and then in the next step drop the `--build` flag.
|
||||||
|
|
||||||
|
3. Build and boot the production image via
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Go to <http://localhost:3000/> and log in.
|
||||||
|
|
||||||
|
5. Navigate around the app and do some stuff and see if it works.
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
- <https://github.com/cotes2020/jekyll-theme-chirpy>
|
- <https://github.com/cotes2020/jekyll-theme-chirpy>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export const DB_PASSWORD = process.env.DB_PASSWORD || ""
|
|||||||
export const DB_DATABASE = process.env.DB_DATABASE || ""
|
export const DB_DATABASE = process.env.DB_DATABASE || ""
|
||||||
export const DB_PORT = parseInt(process.env.DB_PORT || "1433")
|
export const DB_PORT = parseInt(process.env.DB_PORT || "1433")
|
||||||
export const DB_TRUST_SERVER_CERTIFICATE = process.env.DB_TRUST_SERVER_CERTIFICATE === "true"
|
export const DB_TRUST_SERVER_CERTIFICATE = process.env.DB_TRUST_SERVER_CERTIFICATE === "true"
|
||||||
|
export const DB_SSL = process.env.DB_SSL === "true"
|
||||||
|
|
||||||
export const REDIS_CONNECTION_URL = process.env.REDIS_CONNECTION_URL || ""
|
export const REDIS_CONNECTION_URL = process.env.REDIS_CONNECTION_URL || ""
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
DB_HOST,
|
DB_HOST,
|
||||||
DB_PASSWORD,
|
DB_PASSWORD,
|
||||||
DB_PORT,
|
DB_PORT,
|
||||||
|
DB_SSL,
|
||||||
DB_USERNAME,
|
DB_USERNAME,
|
||||||
NODE_ENV,
|
|
||||||
SEQUELIZE_LOGGING,
|
SEQUELIZE_LOGGING,
|
||||||
} from "@/config"
|
} from "@/config"
|
||||||
import compactSql from "@/utils/compact-sql"
|
import compactSql from "@/utils/compact-sql"
|
||||||
@@ -31,7 +31,7 @@ export const SEQUELIZE_CONFIG: Options<PostgresDialect> = {
|
|||||||
password: DB_PASSWORD,
|
password: DB_PASSWORD,
|
||||||
host: DB_HOST,
|
host: DB_HOST,
|
||||||
port: DB_PORT,
|
port: DB_PORT,
|
||||||
ssl: NODE_ENV !== "production" ? false : { rejectUnauthorized: false },
|
ssl: DB_SSL ? { rejectUnauthorized: false } : false,
|
||||||
schema: "public", // default - explicit for clarity
|
schema: "public", // default - explicit for clarity
|
||||||
logging: SEQUELIZE_LOGGING ? sqlLogger : false,
|
logging: SEQUELIZE_LOGGING ? sqlLogger : false,
|
||||||
pool: {
|
pool: {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import path from "path"
|
|||||||
import knex, { Knex } from "knex"
|
import knex, { Knex } from "knex"
|
||||||
import { isEmpty, isNil, merge } from "lodash"
|
import { isEmpty, isNil, merge } from "lodash"
|
||||||
|
|
||||||
import { DB_DATABASE, DB_HOST, DB_PASSWORD, DB_PORT, DB_USERNAME, NODE_ENV } from "@/config"
|
import { DB_DATABASE, DB_HOST, DB_PASSWORD, DB_PORT, DB_SSL, DB_USERNAME, NODE_ENV } from "@/config"
|
||||||
|
|
||||||
if (isEmpty(DB_DATABASE)) throw new Error("database name is unset.")
|
if (isEmpty(DB_DATABASE)) throw new Error("database name is unset.")
|
||||||
if (isEmpty(DB_USERNAME)) throw new Error("database username is unset.")
|
if (isEmpty(DB_USERNAME)) throw new Error("database username is unset.")
|
||||||
@@ -21,17 +21,7 @@ export function buildKnexConfig(options?: Knex.Config): Knex.Config {
|
|||||||
password: DB_PASSWORD,
|
password: DB_PASSWORD,
|
||||||
database: DB_DATABASE,
|
database: DB_DATABASE,
|
||||||
port: DB_PORT,
|
port: DB_PORT,
|
||||||
ssl:
|
ssl: DB_SSL ? { rejectUnauthorized: false } : false,
|
||||||
NODE_ENV !== "production"
|
|
||||||
? false
|
|
||||||
: {
|
|
||||||
require: true, // Enforce SSL
|
|
||||||
rejectUnauthorized: false, // Disable certificate verification (common for Azure)
|
|
||||||
},
|
|
||||||
/* options: {
|
|
||||||
encrypt: true,
|
|
||||||
trustServerCertificate: DB_TRUST_SERVER_CERTIFICATE,
|
|
||||||
}, */
|
|
||||||
},
|
},
|
||||||
migrations: {
|
migrations: {
|
||||||
directory: path.resolve(__dirname, "./migrations"),
|
directory: path.resolve(__dirname, "./migrations"),
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ export class UsersPolicy extends PolicyFactory(User) {
|
|||||||
"displayName",
|
"displayName",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if (this.user.isSystemAdmin) {
|
||||||
|
attributes.push("roles")
|
||||||
|
}
|
||||||
|
|
||||||
return attributes
|
return attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -20,13 +20,13 @@ services:
|
|||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
TZ: "UTC"
|
TZ: "UTC"
|
||||||
POSTGRES_USER: "${DB_USER}"
|
POSTGRES_USER: "${DB_USERNAME}"
|
||||||
POSTGRES_PASSWORD: "${DB_PASS}"
|
POSTGRES_PASSWORD: "${DB_PASSWORD}"
|
||||||
POSTGRES_DB: "${DB_NAME}"
|
POSTGRES_DB: "${DB_DATABASE}"
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- pg-data:/var/lib/postgresql/data
|
- db_data:/var/lib/postgresql/data
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
db_data:
|
db_data:
|
||||||
|
|||||||
Generated
+17
-16
@@ -26,10 +26,10 @@
|
|||||||
"qs": "^6.14.0",
|
"qs": "^6.14.0",
|
||||||
"validator": "^13.15.26",
|
"validator": "^13.15.26",
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
"vue-draggable-next": "^2.3.0",
|
|
||||||
"vue-i18n": "^11.3.0",
|
"vue-i18n": "^11.3.0",
|
||||||
"vue-router": "^4.6.4",
|
"vue-router": "^4.6.4",
|
||||||
"vue-scrollto": "^2.20.0",
|
"vue-scrollto": "^2.20.0",
|
||||||
|
"vuedraggable": "^4.1.0",
|
||||||
"vuetify": "^3.11.4"
|
"vuetify": "^3.11.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -5171,11 +5171,10 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/sortablejs": {
|
"node_modules/sortablejs": {
|
||||||
"version": "1.15.7",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.7.tgz",
|
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
|
||||||
"integrity": "sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==",
|
"integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
@@ -5739,16 +5738,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue-draggable-next": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue-draggable-next/-/vue-draggable-next-2.3.0.tgz",
|
|
||||||
"integrity": "sha512-ymbY0UIwfSdg0iDN/iyNNwUrTqZ/6KbPryzsvTNXBLuDCuOBdNijSK8yynNtmiSj6RapTPQfjLGQdJrZkzBd2w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"sortablejs": "^1.14.0",
|
|
||||||
"vue": "^3.5.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vue-eslint-parser": {
|
"node_modules/vue-eslint-parser": {
|
||||||
"version": "9.4.3",
|
"version": "9.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz",
|
||||||
@@ -5836,6 +5825,18 @@
|
|||||||
"typescript": ">=5.0.0"
|
"typescript": ">=5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vuedraggable": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sortablejs": "1.14.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vuetify": {
|
"node_modules/vuetify": {
|
||||||
"version": "3.12.8",
|
"version": "3.12.8",
|
||||||
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.12.8.tgz",
|
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.12.8.tgz",
|
||||||
|
|||||||
+1
-1
@@ -30,10 +30,10 @@
|
|||||||
"qs": "^6.14.0",
|
"qs": "^6.14.0",
|
||||||
"validator": "^13.15.26",
|
"validator": "^13.15.26",
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
"vue-draggable-next": "^2.3.0",
|
|
||||||
"vue-i18n": "^11.3.0",
|
"vue-i18n": "^11.3.0",
|
||||||
"vue-router": "^4.6.4",
|
"vue-router": "^4.6.4",
|
||||||
"vue-scrollto": "^2.20.0",
|
"vue-scrollto": "^2.20.0",
|
||||||
|
"vuedraggable": "^4.1.0",
|
||||||
"vuetify": "^3.11.4"
|
"vuetify": "^3.11.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="d-flex"
|
class="d-flex"
|
||||||
style="width: 200px"
|
|
||||||
>
|
>
|
||||||
<h1
|
<h1
|
||||||
class="mt-1 ml-3"
|
class="mt-1 ml-3"
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<template>
|
||||||
|
<v-pagination
|
||||||
|
v-model="page"
|
||||||
|
:length="totalPages"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
/**
|
||||||
|
* Use in conjuction with `useRouteQueryPagination` composable.
|
||||||
|
*/
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue"
|
||||||
|
|
||||||
|
const page = defineModel<number>({
|
||||||
|
required: true,
|
||||||
|
default: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
const perPage = defineModel<number>("perPage", {
|
||||||
|
default: 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
totalCount: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const totalPages = computed(() => Math.ceil(props.totalCount / perPage.value))
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog
|
||||||
|
v-model="showDialog"
|
||||||
|
width="400"
|
||||||
|
>
|
||||||
|
<v-form
|
||||||
|
ref="formRef"
|
||||||
|
@submit.prevent="validateAndCreate"
|
||||||
|
>
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>Create New Flashcard Deck</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-text-field
|
||||||
|
v-model="flashcardDeck.name"
|
||||||
|
label="Name"
|
||||||
|
:rules="[required]"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-btn
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
variant="flat"
|
||||||
|
text="Create"
|
||||||
|
/>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-form>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue"
|
||||||
|
import { useRouteQuery } from "@vueuse/router"
|
||||||
|
import { VForm } from "vuetify/components"
|
||||||
|
|
||||||
|
import { required } from "@/utils/validators"
|
||||||
|
import { booleanTransformer } from "@/utils/use-route-query-transformers"
|
||||||
|
|
||||||
|
import useSnack from "@/use/use-snack"
|
||||||
|
import flashcardDecksApi, { FlashcardDeck } from "@/api/flashcard-decks-api"
|
||||||
|
|
||||||
|
const flashcardDeck = ref<Partial<FlashcardDeck>>({})
|
||||||
|
|
||||||
|
const showDialog = useRouteQuery<string, boolean>("showFlashcardDeckCreateDialog", "false", {
|
||||||
|
transform: booleanTransformer,
|
||||||
|
})
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
flashcardDeck.value = {}
|
||||||
|
showDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
showDialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<{ created: [flashcardDeckId: number] }>()
|
||||||
|
|
||||||
|
const isCreating = ref(false)
|
||||||
|
const formRef = ref<InstanceType<typeof VForm> | null>(null)
|
||||||
|
const snack = useSnack()
|
||||||
|
|
||||||
|
async function validateAndCreate() {
|
||||||
|
if (formRef.value === null) return
|
||||||
|
|
||||||
|
const { valid } = await formRef.value.validate()
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
isCreating.value = true
|
||||||
|
const { flashcardDeck: newFlashcardDeck } = await flashcardDecksApi.create(flashcardDeck.value)
|
||||||
|
emit("created", newFlashcardDeck.id)
|
||||||
|
close()
|
||||||
|
snack.success("Flashcard Deck Created")
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
snack.error("Failed to create Flashcard Deck")
|
||||||
|
} finally {
|
||||||
|
isCreating.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show,
|
||||||
|
close,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<v-btn
|
||||||
|
class="ml-3"
|
||||||
|
color="primary"
|
||||||
|
prepend-icon="mdi-book-open-blank-variant-outline"
|
||||||
|
text="Start Review"
|
||||||
|
@click="startReview"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useRouter } from "vue-router"
|
||||||
|
|
||||||
|
const props = defineProps<{ flashcardDeckId: number }>()
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
function startReview() {
|
||||||
|
goToReviewPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToReviewPage() {
|
||||||
|
router.push({
|
||||||
|
name: "FlashcardDeckReviewPage",
|
||||||
|
params: {
|
||||||
|
flashcardDeckId: props.flashcardDeckId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="d-flex align-center justify-space-between mb-1">
|
||||||
|
<span class="title">Decks</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="isLoading"
|
||||||
|
class="d-flex justify-center py-4"
|
||||||
|
>
|
||||||
|
<v-progress-circular
|
||||||
|
indeterminate
|
||||||
|
size="24"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
v-if="tree.length === 0"
|
||||||
|
class="text-center py-6 text-body-2 text-medium-emphasis"
|
||||||
|
>
|
||||||
|
No decks yet. Create one to get started.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<draggable
|
||||||
|
v-model="tree"
|
||||||
|
:group="{ name: 'decks' }"
|
||||||
|
item-key="id"
|
||||||
|
@change="onRootChange"
|
||||||
|
>
|
||||||
|
<template #item="{ element }">
|
||||||
|
<FlashcardDeckTreeNode
|
||||||
|
v-model:children="element.children"
|
||||||
|
:deck="element"
|
||||||
|
:selected-id="selectedId"
|
||||||
|
@select="onSelect"
|
||||||
|
@move="handleMove"
|
||||||
|
@add-child="openCreateDialog"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</draggable>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<v-btn
|
||||||
|
class="ml-2"
|
||||||
|
variant="text"
|
||||||
|
prepend-icon="mdi-plus"
|
||||||
|
text="Add Deck"
|
||||||
|
@click="openCreateDialog(null)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-dialog
|
||||||
|
v-model="showCreateDialog"
|
||||||
|
width="400"
|
||||||
|
>
|
||||||
|
<v-form
|
||||||
|
ref="formRef"
|
||||||
|
@submit.prevent="createDeck"
|
||||||
|
>
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>{{ activeParentId !== null ? "Add Sub-Deck" : "Add Deck" }}</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-text-field
|
||||||
|
v-model="newDeckName"
|
||||||
|
label="Name"
|
||||||
|
autofocus
|
||||||
|
:rules="[required]"
|
||||||
|
/>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn
|
||||||
|
variant="text"
|
||||||
|
text="Cancel"
|
||||||
|
@click="showCreateDialog = false"
|
||||||
|
/>
|
||||||
|
<v-btn
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
variant="flat"
|
||||||
|
:loading="isCreating"
|
||||||
|
text="Create"
|
||||||
|
/>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-form>
|
||||||
|
</v-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import type { FlashcardDeck } from "@/api/flashcard-decks-api"
|
||||||
|
|
||||||
|
export type DeckNode = FlashcardDeck & { children: DeckNode[] }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from "vue"
|
||||||
|
import draggable from "vuedraggable"
|
||||||
|
import { VForm } from "vuetify/components"
|
||||||
|
|
||||||
|
import flashcardDecksApi from "@/api/flashcard-decks-api"
|
||||||
|
import { required } from "@/utils/validators"
|
||||||
|
|
||||||
|
import useFlashcardDecks from "@/use/use-flashcard-decks"
|
||||||
|
import useSnack from "@/use/use-snack"
|
||||||
|
|
||||||
|
import FlashcardDeckTreeNode from "@/components/flashcard-decks/FlashcardDeckTreeNode.vue"
|
||||||
|
|
||||||
|
const { flashcardDecks, isLoading, fetch } = useFlashcardDecks()
|
||||||
|
|
||||||
|
const tree = ref<DeckNode[]>([])
|
||||||
|
|
||||||
|
function buildTreeOptimized(decks: FlashcardDeck[]): DeckNode[] {
|
||||||
|
const map = new Map<number, DeckNode>()
|
||||||
|
const roots: DeckNode[] = []
|
||||||
|
|
||||||
|
decks.forEach((d) => map.set(d.id, { ...d, children: [] }))
|
||||||
|
|
||||||
|
decks.forEach((d) => {
|
||||||
|
const node = map.get(d.id)!
|
||||||
|
if (d.parentDeckId === null) {
|
||||||
|
roots.push(node)
|
||||||
|
} else {
|
||||||
|
const parent = map.get(d.parentDeckId)
|
||||||
|
if (parent) parent.children.push(node)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return roots
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
flashcardDecks,
|
||||||
|
(newDecks) => {
|
||||||
|
tree.value = buildTreeOptimized(newDecks)
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- Selection ---
|
||||||
|
|
||||||
|
const selectedId = ref<number | null>(null)
|
||||||
|
const emit = defineEmits<{ select: [deck: DeckNode] }>()
|
||||||
|
|
||||||
|
function onSelect(deck: DeckNode) {
|
||||||
|
selectedId.value = deck.id
|
||||||
|
emit("select", deck)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Drag & Drop ---
|
||||||
|
|
||||||
|
const snack = useSnack()
|
||||||
|
|
||||||
|
async function handleMove({ deckId, newParentId }: { deckId: number; newParentId: number | null }) {
|
||||||
|
try {
|
||||||
|
await flashcardDecksApi.update(deckId, { parentDeckId: newParentId })
|
||||||
|
// await fetch()
|
||||||
|
} catch {
|
||||||
|
snack.error("Failed to move deck")
|
||||||
|
tree.value = buildTreeOptimized(flashcardDecks.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRootChange(event: { added?: { element: DeckNode } }) {
|
||||||
|
if (event.added) {
|
||||||
|
handleMove({ deckId: event.added.element.id, newParentId: null })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Create ---
|
||||||
|
|
||||||
|
const showCreateDialog = ref(false)
|
||||||
|
const activeParentId = ref<number | null>(null)
|
||||||
|
const newDeckName = ref("")
|
||||||
|
const isCreating = ref(false)
|
||||||
|
const formRef = ref<InstanceType<typeof VForm> | null>(null)
|
||||||
|
|
||||||
|
function openCreateDialog(parentId: number | null) {
|
||||||
|
activeParentId.value = parentId
|
||||||
|
newDeckName.value = ""
|
||||||
|
showCreateDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createDeck() {
|
||||||
|
if (!formRef.value) return
|
||||||
|
const { valid } = await formRef.value.validate()
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
isCreating.value = true
|
||||||
|
await flashcardDecksApi.create({
|
||||||
|
name: newDeckName.value,
|
||||||
|
parentDeckId: activeParentId.value,
|
||||||
|
})
|
||||||
|
showCreateDialog.value = false
|
||||||
|
await fetch()
|
||||||
|
} catch {
|
||||||
|
snack.error("Failed to create deck")
|
||||||
|
} finally {
|
||||||
|
isCreating.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ refresh: fetch })
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="deck-node d-flex align-center px-3 py-2 rounded"
|
||||||
|
:class="{ selected: isSelected }"
|
||||||
|
@click="$emit('select', deck)"
|
||||||
|
>
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
variant="text"
|
||||||
|
density="comfortable"
|
||||||
|
size="default"
|
||||||
|
class="mr-1"
|
||||||
|
@click.stop="expanded = !expanded"
|
||||||
|
>
|
||||||
|
<v-icon size="default">
|
||||||
|
{{
|
||||||
|
hasChildren ? (expanded ? "mdi-chevron-down" : "mdi-chevron-right") : "mdi-circle-small"
|
||||||
|
}}
|
||||||
|
</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-icon
|
||||||
|
size="default"
|
||||||
|
class="mr-2 text-medium-emphasis"
|
||||||
|
:icon="expanded && hasChildren ? 'mdi-folder-open-outline' : 'mdi-folder-outline'"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="flex-grow-1 text-body-1"
|
||||||
|
:class="{ 'font-weight-medium': isSelected }"
|
||||||
|
>
|
||||||
|
{{ deck.name }}
|
||||||
|
</span>
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
variant="text"
|
||||||
|
density="comfortable"
|
||||||
|
size="default"
|
||||||
|
title="Add sub-deck"
|
||||||
|
class="add-btn"
|
||||||
|
@click.stop="$emit('add-child', deck.id)"
|
||||||
|
>
|
||||||
|
<v-icon
|
||||||
|
size="default"
|
||||||
|
icon="mdi-plus"
|
||||||
|
/>
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-show="expanded"
|
||||||
|
class="pl-5"
|
||||||
|
>
|
||||||
|
<draggable
|
||||||
|
v-model="children"
|
||||||
|
:group="{ name: 'decks' }"
|
||||||
|
item-key="id"
|
||||||
|
@change="onChange"
|
||||||
|
>
|
||||||
|
<template #item="{ element }">
|
||||||
|
<FlashcardDeckTreeNode
|
||||||
|
v-model:children="element.children"
|
||||||
|
:deck="element"
|
||||||
|
:selected-id="selectedId"
|
||||||
|
@select="$emit('select', $event)"
|
||||||
|
@move="$emit('move', $event)"
|
||||||
|
@add-child="$emit('add-child', $event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from "vue"
|
||||||
|
import draggable from "vuedraggable"
|
||||||
|
|
||||||
|
import type { DeckNode } from "@/components/flashcard-decks/FlashcardDeckTree.vue"
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
deck: DeckNode
|
||||||
|
selectedId?: number | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const children = defineModel<DeckNode[]>("children", { required: true })
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
select: [deck: DeckNode]
|
||||||
|
move: [payload: { deckId: number; newParentId: number | null }]
|
||||||
|
"add-child": [parentId: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const expanded = ref(false)
|
||||||
|
const isSelected = computed(() => props.selectedId === props.deck.id)
|
||||||
|
const hasChildren = computed(() => children.value.length > 0)
|
||||||
|
|
||||||
|
function onChange(event: { added?: { element: DeckNode } }) {
|
||||||
|
if (event.added) {
|
||||||
|
emit("move", { deckId: event.added.element.id, newParentId: props.deck.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.deck-node {
|
||||||
|
cursor: grab;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deck-node:hover {
|
||||||
|
background: rgb(var(--v-theme-hoverColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
.deck-node.selected {
|
||||||
|
background: rgb(var(--v-theme-lightprimary));
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deck-node:hover .add-btn {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
<template>
|
||||||
|
<v-data-table-server
|
||||||
|
v-model:items-per-page="perPage"
|
||||||
|
v-model:page="page"
|
||||||
|
v-model:sort-by="sortBy"
|
||||||
|
:headers="headers"
|
||||||
|
:items="flashcardDecks"
|
||||||
|
:items-length="totalCount"
|
||||||
|
:loading="isLoading"
|
||||||
|
@click:row="rowClicked"
|
||||||
|
@update:page="updatePage"
|
||||||
|
>
|
||||||
|
<template
|
||||||
|
v-for="(_, name) in $slots"
|
||||||
|
:key="name"
|
||||||
|
#[name]="slotProps"
|
||||||
|
>
|
||||||
|
<slot
|
||||||
|
:name="name"
|
||||||
|
v-bind="slotProps"
|
||||||
|
></slot>
|
||||||
|
</template>
|
||||||
|
</v-data-table-server>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export const DEFAULT_HEADERS = [
|
||||||
|
{ title: "Name", key: "name", align: "start" as const, sortable: true },
|
||||||
|
{ title: "Created At", key: "createdAt", align: "start" as const, sortable: true },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue"
|
||||||
|
|
||||||
|
import useVuetifySortByToSafeRouteQuery, {
|
||||||
|
type SortItem,
|
||||||
|
} from "@/use/utils/use-vuetify-sort-by-to-safe-route-query"
|
||||||
|
import useVuetifySortByToSequelizeSafeOrder from "@/use/utils/use-vuetify-sort-by-to-sequelize-safe-order"
|
||||||
|
import useRouteQueryPagination from "@/use/utils/use-route-query-pagination"
|
||||||
|
import useFlashcardDecks, {
|
||||||
|
type FlashcardDeck,
|
||||||
|
type FlashcardDeckFiltersOptions,
|
||||||
|
type FlashcardDeckQueryOptions,
|
||||||
|
type FlashcardDeckWhereOptions,
|
||||||
|
} from "@/use/use-flashcard-decks"
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
headers?: { title: string; key: string }[]
|
||||||
|
filters?: FlashcardDeckFiltersOptions
|
||||||
|
where?: FlashcardDeckWhereOptions
|
||||||
|
sortBy?: SortItem[]
|
||||||
|
routeQuerySuffix?: string
|
||||||
|
waiting?: boolean
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
headers: () => DEFAULT_HEADERS,
|
||||||
|
filters: () => ({}),
|
||||||
|
where: () => ({}),
|
||||||
|
sortBy: () => [],
|
||||||
|
routeQuerySuffix: "FlashcardDecks",
|
||||||
|
waiting: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const { page, perPage } = useRouteQueryPagination({ routeQuerySuffix: props.routeQuerySuffix })
|
||||||
|
const sortBy = useVuetifySortByToSafeRouteQuery(`sortBy${props.routeQuerySuffix}`, props.sortBy)
|
||||||
|
const order = useVuetifySortByToSequelizeSafeOrder(sortBy)
|
||||||
|
|
||||||
|
const queryOptions = computed<FlashcardDeckQueryOptions>(() => ({
|
||||||
|
where: props.where,
|
||||||
|
filters: props.filters,
|
||||||
|
order: order.value,
|
||||||
|
page: page.value,
|
||||||
|
perPage: perPage.value,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { flashcardDecks, totalCount, isLoading, refresh } = useFlashcardDecks(queryOptions, {
|
||||||
|
skipWatchIf: () => props.waiting,
|
||||||
|
})
|
||||||
|
|
||||||
|
type DeckTableRow = {
|
||||||
|
item: FlashcardDeck
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<{ clicked: [deck: FlashcardDeck] }>()
|
||||||
|
|
||||||
|
function rowClicked(_event: unknown, row: DeckTableRow) {
|
||||||
|
emit("clicked", row.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePage(newPage: number) {
|
||||||
|
if (isLoading.value || props.waiting) return
|
||||||
|
|
||||||
|
page.value = newPage
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ refresh, totalCount })
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<v-card class="pa-5">
|
||||||
|
<div
|
||||||
|
class="face-content text-h5 text-center"
|
||||||
|
v-html="renderMarkdown(flashcard.front)"
|
||||||
|
/>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { type Flashcard } from "@/api/flashcards-api"
|
||||||
|
import renderMarkdown from "@/utils/render-markdown"
|
||||||
|
|
||||||
|
defineProps<{ flashcard: Flashcard }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.face-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog
|
||||||
|
v-model="showDialog"
|
||||||
|
width="500"
|
||||||
|
>
|
||||||
|
<v-form
|
||||||
|
ref="formRef"
|
||||||
|
@submit.prevent="validateAndCreate"
|
||||||
|
>
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>Add Flashcard</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-textarea
|
||||||
|
v-model="flashcard.front"
|
||||||
|
label="Front"
|
||||||
|
rows="3"
|
||||||
|
:rules="[required]"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-textarea
|
||||||
|
v-model="flashcard.back"
|
||||||
|
label="Back"
|
||||||
|
rows="3"
|
||||||
|
:rules="[required]"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn
|
||||||
|
variant="text"
|
||||||
|
text="Cancel"
|
||||||
|
@click="close"
|
||||||
|
/>
|
||||||
|
<v-btn
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
variant="flat"
|
||||||
|
:loading="isCreating"
|
||||||
|
text="Create"
|
||||||
|
/>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-form>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue"
|
||||||
|
import { VForm } from "vuetify/components"
|
||||||
|
|
||||||
|
import { required } from "@/utils/validators"
|
||||||
|
import useSnack from "@/use/use-snack"
|
||||||
|
import flashcardsApi, { type Flashcard } from "@/api/flashcards-api"
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
flashcardDeckId: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{ created: [flashcardId: number] }>()
|
||||||
|
|
||||||
|
const showDialog = ref(false)
|
||||||
|
const isCreating = ref(false)
|
||||||
|
const formRef = ref<InstanceType<typeof VForm> | null>(null)
|
||||||
|
const flashcard = ref<Partial<Flashcard>>({})
|
||||||
|
const snack = useSnack()
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
flashcard.value = {}
|
||||||
|
showDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
showDialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validateAndCreate() {
|
||||||
|
if (!formRef.value) return
|
||||||
|
|
||||||
|
const { valid } = await formRef.value.validate()
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
isCreating.value = true
|
||||||
|
const { flashcard: newFlashcard } = await flashcardsApi.create({
|
||||||
|
...flashcard.value,
|
||||||
|
flashcardDeckId: props.flashcardDeckId,
|
||||||
|
})
|
||||||
|
emit("created", newFlashcard.id)
|
||||||
|
close()
|
||||||
|
snack.success("Flashcard created")
|
||||||
|
} catch {
|
||||||
|
snack.error("Failed to create flashcard")
|
||||||
|
} finally {
|
||||||
|
isCreating.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ show, close })
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flashcard-scene">
|
||||||
|
<div
|
||||||
|
class="flashcard"
|
||||||
|
:class="{ 'is-flipped': isFlipped }"
|
||||||
|
@click="flip"
|
||||||
|
>
|
||||||
|
<!-- Front -->
|
||||||
|
<v-card
|
||||||
|
class="flashcard-face flashcard-face--front"
|
||||||
|
rounded="xl"
|
||||||
|
elevation="4"
|
||||||
|
>
|
||||||
|
<div class="face-label text-textSecondary text-caption text-uppercase font-weight-bold">
|
||||||
|
Question
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="face-content text-h5 text-center"
|
||||||
|
v-html="renderMarkdown(flashcard.front)"
|
||||||
|
/>
|
||||||
|
<div class="face-hint text-textSecondary text-caption">Click to reveal</div>
|
||||||
|
</v-card>
|
||||||
|
|
||||||
|
<!-- Back -->
|
||||||
|
<v-card
|
||||||
|
class="flashcard-face flashcard-face--back"
|
||||||
|
color="white"
|
||||||
|
rounded="xl"
|
||||||
|
elevation="4"
|
||||||
|
>
|
||||||
|
<div class="face-label text-textSecondary text-caption text-uppercase font-weight-bold">
|
||||||
|
Answer
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="face-content text-h5 text-center"
|
||||||
|
v-html="renderMarkdown(flashcard.back)"
|
||||||
|
/>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue"
|
||||||
|
import { type Flashcard } from "@/api/flashcards-api"
|
||||||
|
|
||||||
|
import renderMarkdown from "@/utils/render-markdown"
|
||||||
|
|
||||||
|
defineProps<{ flashcard: Flashcard }>()
|
||||||
|
|
||||||
|
const isFlipped = ref(false)
|
||||||
|
|
||||||
|
function flip() {
|
||||||
|
isFlipped.value = !isFlipped.value
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.flashcard-scene {
|
||||||
|
perspective: 1200px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 640px;
|
||||||
|
height: 380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flashcard {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flashcard.is-flipped {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flashcard-face {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
-webkit-backface-visibility: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px 48px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flashcard-face--back {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
color: black !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flashcard-face--back :deep(*) {
|
||||||
|
color: black !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.face-label {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
left: 28px;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.face-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
.face-hint {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.face-actions {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<v-data-table-server
|
||||||
|
v-model:items-per-page="perPage"
|
||||||
|
v-model:page="page"
|
||||||
|
v-model:sort-by="sortBy"
|
||||||
|
:headers="headers"
|
||||||
|
:items="flashcards"
|
||||||
|
:items-length="totalCount"
|
||||||
|
:loading="isLoading"
|
||||||
|
@click:row="rowClicked"
|
||||||
|
@update:page="updatePage"
|
||||||
|
>
|
||||||
|
<template #item.front="{ item }">
|
||||||
|
<span class="text-truncate d-inline-block">{{ item.front }}</span>
|
||||||
|
</template>
|
||||||
|
<template #item.back="{ item }">
|
||||||
|
<span class="text-truncate d-inline-block">{{ item.back }}</span>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
v-for="(_, name) in $slots"
|
||||||
|
:key="name"
|
||||||
|
#[name]="slotProps"
|
||||||
|
>
|
||||||
|
<slot
|
||||||
|
:name="name"
|
||||||
|
v-bind="slotProps"
|
||||||
|
></slot>
|
||||||
|
</template>
|
||||||
|
</v-data-table-server>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export const DEFAULT_HEADERS = [
|
||||||
|
{ title: "Front", key: "front", align: "start" as const, sortable: false },
|
||||||
|
{ title: "Back", key: "back", align: "start" as const, sortable: false },
|
||||||
|
{ title: "Created At", key: "createdAt", align: "start" as const, sortable: true },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue"
|
||||||
|
|
||||||
|
import useVuetifySortByToSafeRouteQuery, {
|
||||||
|
type SortItem,
|
||||||
|
} from "@/use/utils/use-vuetify-sort-by-to-safe-route-query"
|
||||||
|
import useVuetifySortByToSequelizeSafeOrder from "@/use/utils/use-vuetify-sort-by-to-sequelize-safe-order"
|
||||||
|
import useRouteQueryPagination from "@/use/utils/use-route-query-pagination"
|
||||||
|
import useFlashcards, {
|
||||||
|
type Flashcard,
|
||||||
|
type FlashcardFiltersOptions,
|
||||||
|
type FlashcardQueryOptions,
|
||||||
|
type FlashcardWhereOptions,
|
||||||
|
} from "@/use/use-flashcards"
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
headers?: { title: string; key: string }[]
|
||||||
|
filters?: FlashcardFiltersOptions
|
||||||
|
where?: FlashcardWhereOptions
|
||||||
|
sortBy?: SortItem[]
|
||||||
|
routeQuerySuffix?: string
|
||||||
|
waiting?: boolean
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
headers: () => DEFAULT_HEADERS,
|
||||||
|
filters: () => ({}),
|
||||||
|
where: () => ({}),
|
||||||
|
sortBy: () => [],
|
||||||
|
routeQuerySuffix: "Flashcards",
|
||||||
|
waiting: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const { page, perPage } = useRouteQueryPagination({ routeQuerySuffix: props.routeQuerySuffix })
|
||||||
|
const sortBy = useVuetifySortByToSafeRouteQuery(`sortBy${props.routeQuerySuffix}`, props.sortBy)
|
||||||
|
const order = useVuetifySortByToSequelizeSafeOrder(sortBy)
|
||||||
|
|
||||||
|
const queryOptions = computed<FlashcardQueryOptions>(() => ({
|
||||||
|
where: props.where,
|
||||||
|
filters: props.filters,
|
||||||
|
order: order.value,
|
||||||
|
page: page.value,
|
||||||
|
perPage: perPage.value,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { flashcards, totalCount, isLoading, refresh } = useFlashcards(queryOptions, {
|
||||||
|
skipWatchIf: () => props.waiting,
|
||||||
|
})
|
||||||
|
|
||||||
|
type FlashcardTableRow = {
|
||||||
|
item: Flashcard
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<{ clicked: [flashcard: Flashcard] }>()
|
||||||
|
|
||||||
|
function rowClicked(_event: unknown, row: FlashcardTableRow) {
|
||||||
|
emit("clicked", row.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePage(newPage: number) {
|
||||||
|
if (isLoading.value || props.waiting) return
|
||||||
|
|
||||||
|
page.value = newPage
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ refresh, totalCount })
|
||||||
|
</script>
|
||||||
@@ -9,6 +9,20 @@
|
|||||||
<AppLogo />
|
<AppLogo />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<v-tabs
|
||||||
|
class="ml-2"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<v-tab
|
||||||
|
:to="{ name: 'DashboardPage' }"
|
||||||
|
text="Dashboard"
|
||||||
|
/>
|
||||||
|
<v-tab
|
||||||
|
:to="{ name: 'FlashcardsPage' }"
|
||||||
|
text="Flashcards"
|
||||||
|
/>
|
||||||
|
</v-tabs>
|
||||||
|
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
|
|
||||||
<!-- TODO Add notifications -->
|
<!-- TODO Add notifications -->
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<v-row class="fill-height">
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
md="3"
|
||||||
|
>
|
||||||
|
<v-card
|
||||||
|
height="100%"
|
||||||
|
class="pa-2"
|
||||||
|
>
|
||||||
|
<FlashcardDeckTree
|
||||||
|
ref="deckTree"
|
||||||
|
@select="onDeckSelected"
|
||||||
|
/>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
md="9"
|
||||||
|
>
|
||||||
|
<template v-if="selectedDeck">
|
||||||
|
<div class="d-flex align-center justify-space-between mb-4">
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
variant="tonal"
|
||||||
|
prepend-icon="mdi-plus"
|
||||||
|
text="Add Flashcard"
|
||||||
|
@click="openFlashcardCreateDialog"
|
||||||
|
/>
|
||||||
|
<FlashcardDeckStartReviewBtn :flashcard-deck-id="selectedDeck.id" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-divider class="my-5" />
|
||||||
|
|
||||||
|
<v-row>
|
||||||
|
<v-col
|
||||||
|
v-for="flashcard in flashcards"
|
||||||
|
:key="flashcard.id"
|
||||||
|
cols="12"
|
||||||
|
md="6"
|
||||||
|
lg="4"
|
||||||
|
>
|
||||||
|
<FlashcardCard :flashcard="flashcard" />
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<EnhancedPagination
|
||||||
|
v-model="page"
|
||||||
|
v-model:per-page="perPage"
|
||||||
|
:total-count="totalCount"
|
||||||
|
class="mt-4"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FlashcardCreateDialog
|
||||||
|
v-if="selectedDeck"
|
||||||
|
ref="flashcardCreateDialog"
|
||||||
|
:flashcard-deck-id="selectedDeck.id"
|
||||||
|
@created="refreshFlashcards"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="d-flex align-center justify-center h-100 text-medium-emphasis"
|
||||||
|
>
|
||||||
|
Select a deck to view its flashcards
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from "vue"
|
||||||
|
|
||||||
|
import useBreadcrumbs from "@/use/use-breadcrumbs"
|
||||||
|
import useFlashcards from "@/use/use-flashcards"
|
||||||
|
import useRouteQueryPagination from "@/use/utils/use-route-query-pagination"
|
||||||
|
|
||||||
|
import FlashcardDeckTree, {
|
||||||
|
type DeckNode,
|
||||||
|
} from "@/components/flashcard-decks/FlashcardDeckTree.vue"
|
||||||
|
import FlashcardCard from "@/components/flashcards/FlashcardCard.vue"
|
||||||
|
import FlashcardCreateDialog from "@/components/flashcards/FlashcardCreateDialog.vue"
|
||||||
|
import FlashcardDeckStartReviewBtn from "@/components/flashcard-decks/FlashcardDeckStartReviewBtn.vue"
|
||||||
|
import EnhancedPagination from "@/components/common/EnhancedPagination.vue"
|
||||||
|
|
||||||
|
const deckTree = ref<InstanceType<typeof FlashcardDeckTree> | null>(null)
|
||||||
|
const selectedDeck = ref<DeckNode | null>(null)
|
||||||
|
const flashcardCreateDialog = ref<InstanceType<typeof FlashcardCreateDialog> | null>(null)
|
||||||
|
|
||||||
|
const { page, perPage } = useRouteQueryPagination({ perPage: 4 })
|
||||||
|
|
||||||
|
const flashcardsQueryOptions = computed(() => ({
|
||||||
|
where: { flashcardDeckId: selectedDeck.value?.id },
|
||||||
|
page: page.value,
|
||||||
|
perPage: perPage.value,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const {
|
||||||
|
flashcards,
|
||||||
|
totalCount,
|
||||||
|
refresh: refreshFlashcards,
|
||||||
|
} = useFlashcards(flashcardsQueryOptions, {
|
||||||
|
skipWatchIf: () => selectedDeck.value === null,
|
||||||
|
})
|
||||||
|
|
||||||
|
function onDeckSelected(deck: DeckNode) {
|
||||||
|
selectedDeck.value = deck
|
||||||
|
}
|
||||||
|
|
||||||
|
function openFlashcardCreateDialog() {
|
||||||
|
flashcardCreateDialog.value?.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
useBreadcrumbs("Study")
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="d-flex">
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn
|
||||||
|
class="mt-2"
|
||||||
|
variant="tonal"
|
||||||
|
prepend-icon="mdi-shuffle"
|
||||||
|
text="Shuffle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-skeleton-loader
|
||||||
|
v-if="isLoading"
|
||||||
|
type="card"
|
||||||
|
/>
|
||||||
|
<div v-else-if="isEmpty(flashcards)"></div>
|
||||||
|
<div
|
||||||
|
v-else-if="isEmpty(queue)"
|
||||||
|
class="review-layout mt-10"
|
||||||
|
>
|
||||||
|
<v-icon
|
||||||
|
size="64"
|
||||||
|
color="success"
|
||||||
|
>
|
||||||
|
mdi-check-circle-outline
|
||||||
|
</v-icon>
|
||||||
|
<div class="text-h5">Done!</div>
|
||||||
|
<div class="text-medium-emphasis">
|
||||||
|
{{ numberOfCorrect }} correct • {{ numberOfIncorrect }} incorrect
|
||||||
|
</div>
|
||||||
|
<div class="text-medium-emphasis text-caption">
|
||||||
|
{{ elapsedTime }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="review-layout"
|
||||||
|
>
|
||||||
|
<FlashcardReviewCard
|
||||||
|
:key="queue[currentIndex].id"
|
||||||
|
class="mt-5"
|
||||||
|
:flashcard="queue[currentIndex]"
|
||||||
|
/>
|
||||||
|
<div class="review-nav">
|
||||||
|
<v-btn
|
||||||
|
icon="mdi-chevron-left"
|
||||||
|
variant="text"
|
||||||
|
size="x-large"
|
||||||
|
:disabled="currentIndex === 0"
|
||||||
|
@click="prev"
|
||||||
|
/>
|
||||||
|
<v-btn
|
||||||
|
class="mr-3"
|
||||||
|
color="error"
|
||||||
|
variant="tonal"
|
||||||
|
prepend-icon="mdi-close"
|
||||||
|
@click="markCurrentCardIncorrect"
|
||||||
|
>
|
||||||
|
{{ numberOfIncorrect }}
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
class="ml-3"
|
||||||
|
color="success"
|
||||||
|
variant="tonal"
|
||||||
|
append-icon="mdi-check"
|
||||||
|
@click="markCurrentCardCorrect"
|
||||||
|
>
|
||||||
|
{{ numberOfCorrect }}
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
icon="mdi-chevron-right"
|
||||||
|
variant="text"
|
||||||
|
size="x-large"
|
||||||
|
:disabled="currentIndex === queue.length - 1"
|
||||||
|
@click="next"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { isEmpty, isNil } from "lodash"
|
||||||
|
import { computed, ref, watch } from "vue"
|
||||||
|
|
||||||
|
import useFlashcardDeck from "@/use/use-flashcard-deck"
|
||||||
|
import useFlashcards, { type Flashcard, FlashcardQueryOptions } from "@/use/use-flashcards"
|
||||||
|
|
||||||
|
import FlashcardReviewCard from "@/components/flashcards/FlashcardReviewCard.vue"
|
||||||
|
|
||||||
|
const props = defineProps<{ flashcardDeckId: string }>()
|
||||||
|
|
||||||
|
const flashcardDeckIdAsNumber = computed(() => parseInt(props.flashcardDeckId))
|
||||||
|
const { flashcardDeck } = useFlashcardDeck(flashcardDeckIdAsNumber)
|
||||||
|
|
||||||
|
const flashcardsQueryOptions = computed<FlashcardQueryOptions>(() => {
|
||||||
|
return {
|
||||||
|
where: {
|
||||||
|
flashcardDeckId: flashcardDeck.value?.id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { flashcards, isLoading } = useFlashcards(flashcardsQueryOptions, {
|
||||||
|
skipWatchIf: () => isNil(flashcardDeck.value),
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentIndex = ref(0)
|
||||||
|
const queue = ref<Flashcard[]>([])
|
||||||
|
const results = ref(new Map<number, "correct" | "incorrect">())
|
||||||
|
const startTime = ref<number | null>(null)
|
||||||
|
const endTime = ref<number | null>(null)
|
||||||
|
|
||||||
|
const elapsedTime = computed(() => {
|
||||||
|
if (startTime.value === null || endTime.value === null) return ""
|
||||||
|
const seconds = Math.floor((endTime.value - startTime.value) / 1000)
|
||||||
|
const m = Math.floor(seconds / 60)
|
||||||
|
const s = seconds % 60
|
||||||
|
return m > 0 ? `${m}m ${s}s` : `${s}s`
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
flashcards,
|
||||||
|
(newFlashcards) => {
|
||||||
|
queue.value = [...newFlashcards]
|
||||||
|
if (newFlashcards.length > 0) startTime.value = Date.now()
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const numberOfCorrect = computed(
|
||||||
|
() => [...results.value.values()].filter((r) => r === "correct").length
|
||||||
|
)
|
||||||
|
const numberOfIncorrect = computed(
|
||||||
|
() => [...results.value.values()].filter((r) => r === "incorrect").length
|
||||||
|
)
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
if (currentIndex.value < queue.value.length - 1) {
|
||||||
|
currentIndex.value++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function prev() {
|
||||||
|
if (currentIndex.value > 0) {
|
||||||
|
currentIndex.value--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function markCurrentCard(result: "correct" | "incorrect") {
|
||||||
|
const card = queue.value[currentIndex.value]
|
||||||
|
results.value = new Map(results.value).set(card.id, result)
|
||||||
|
queue.value.splice(currentIndex.value, 1)
|
||||||
|
if (queue.value.length === 0) {
|
||||||
|
endTime.value = Date.now()
|
||||||
|
} else if (currentIndex.value >= queue.value.length) {
|
||||||
|
currentIndex.value--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function markCurrentCardCorrect() {
|
||||||
|
markCurrentCard("correct")
|
||||||
|
}
|
||||||
|
|
||||||
|
function markCurrentCardIncorrect() {
|
||||||
|
markCurrentCard("incorrect")
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.review-layout {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -34,6 +34,23 @@ const routes: RouteRecordRaw[] = [
|
|||||||
title: "Dashboard",
|
title: "Dashboard",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "FlashcardsPage",
|
||||||
|
path: "flashcards",
|
||||||
|
component: () => import("@/pages/FlashcardsPage.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "Flashcards",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "FlashcardDeckReviewPage",
|
||||||
|
path: "flashcard-deck/:flashcardDeckId/review",
|
||||||
|
component: () => import("@/pages/flashcard-deck-reviews/FlashcardDeckReviewPage.vue"),
|
||||||
|
props: true,
|
||||||
|
meta: {
|
||||||
|
title: "Deck review",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
component: () => import("@/layouts/LayoutWithBreadcrumbs.vue"),
|
component: () => import("@/layouts/LayoutWithBreadcrumbs.vue"),
|
||||||
|
|||||||
+95
-107
@@ -1,204 +1,192 @@
|
|||||||
@use "./variables" as *;
|
@use "./variables" as *;
|
||||||
|
|
||||||
html {
|
html {
|
||||||
.bg-success {
|
.bg-success {
|
||||||
color: $white !important;
|
color: $white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-primary {
|
.bg-primary {
|
||||||
color: $white !important;
|
color: $white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-secondary {
|
.bg-secondary {
|
||||||
color: $white !important;
|
color: $white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-warning {
|
.bg-warning {
|
||||||
color: $white !important;
|
color: $white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-secondary-gradient {
|
.bg-secondary-gradient {
|
||||||
background: linear-gradient(287deg, rgb(var(--v-theme-primary)) .54%, #1bcaff 100.84%);
|
background: linear-gradient(287deg, rgb(var(--v-theme-primary)) 0.54%, #1bcaff 100.84%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.border,
|
.border,
|
||||||
.v-divider {
|
.v-divider {
|
||||||
border-color: rgba(var(--v-border-color)) !important;
|
border-color: rgba(var(--v-border-color)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avtar-border {
|
.avtar-border {
|
||||||
border: 2px solid rgb(var(--v-theme-surface)) !important;
|
border: 2px solid rgb(var(--v-theme-surface)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtext {
|
.subtext {
|
||||||
font-size: $font-size-root;
|
font-size: $font-size-root;
|
||||||
line-height: 1.75rem;
|
line-height: 1.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-dialog {
|
.v-dialog {
|
||||||
&.dialog-mw {
|
&.dialog-mw {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.round-40 {
|
.round-40 {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.round-56 {
|
.round-56 {
|
||||||
height: 56px;
|
height: 56px;
|
||||||
width: 56px;
|
width: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.round-48 {
|
.round-48 {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
width: 48px;
|
width: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.round-30 {
|
.round-30 {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lh-0 {
|
.lh-0 {
|
||||||
line-height: 0 !important;
|
line-height: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.lh-28 {
|
.lh-28 {
|
||||||
line-height: 28px !important;
|
line-height: 28px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lh-32 {
|
.lh-32 {
|
||||||
line-height: 32px !important;
|
line-height: 32px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.space-p-96 {
|
.space-p-96 {
|
||||||
padding: 96px 0 !important;
|
padding: 96px 0 !important;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ps-96 {
|
.ps-96 {
|
||||||
padding-inline-start: 96px !important;
|
padding-inline-start: 96px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pt-96 {
|
.pt-96 {
|
||||||
padding-top: 96px !important;
|
padding-top: 96px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.end-0 {
|
.end-0 {
|
||||||
inset-inline-end: 0;
|
inset-inline-end: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-0 {
|
.top-0 {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-scrollbar {
|
.no-scrollbar {
|
||||||
height: calc(100vh - 350px);
|
height: calc(100vh - 350px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.msg-chat-height {
|
.msg-chat-height {
|
||||||
height: calc(-500px + 100vh);
|
height: calc(-500px + 100vh);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width:991px) {
|
@media screen and (max-width: 991px) {
|
||||||
.overflow-x-reposive {
|
.overflow-x-reposive {
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-m-none {
|
.border-m-none {
|
||||||
border: 0 !important
|
border: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-height:767px) {
|
@media screen and (max-height: 767px) {
|
||||||
.msg-chat-height {
|
.msg-chat-height {
|
||||||
height: calc(-315px + 100vh);
|
height: calc(-315px + 100vh);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.max-h-600 {
|
.max-h-600 {
|
||||||
max-height: 600px;
|
max-height: 600px;
|
||||||
height: calc(100vh - 100px);
|
height: calc(100vh - 100px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.custom-hover-primary {
|
.custom-hover-primary {
|
||||||
|
.iconify {
|
||||||
|
color: rgb(255, 255, 255) !important;
|
||||||
|
|
||||||
|
@media screen and (max-width: 991px) {
|
||||||
|
color: rgba(var(--v-theme-textPrimary), 0.8) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(var(--v-theme-lightprimary), 0.1);
|
||||||
|
|
||||||
.iconify {
|
.iconify {
|
||||||
color: rgb(255, 255, 255) !important;
|
@media screen and (max-width: 991px) {
|
||||||
|
color: rgb(var(--v-theme-primary)) !important;
|
||||||
@media screen and (max-width:991px) {
|
}
|
||||||
color: rgba(var(--v-theme-textPrimary), 0.8) !important
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: rgba(var(--v-theme-lightprimary), 0.1);
|
|
||||||
|
|
||||||
.iconify {
|
|
||||||
@media screen and (max-width:991px) {
|
|
||||||
color: rgb(var(--v-theme-primary)) !important
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.custom-hover-primary-white {
|
.custom-hover-primary-white {
|
||||||
.iconify {
|
.iconify {
|
||||||
color: rgb(255, 255, 255) !important;
|
color: rgb(255, 255, 255) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
}
|
background-color: rgba(var(--v-theme-lightprimary), 0.1);
|
||||||
|
}
|
||||||
&:hover {
|
|
||||||
background-color: rgba(var(--v-theme-lightprimary), 0.1);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-icon {
|
.no-icon {
|
||||||
|
.v-input__prepend,
|
||||||
.v-input__prepend,
|
.v-input__append {
|
||||||
.v-input__append {
|
display: none !important;
|
||||||
display: none !important;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-white {
|
.bg-white {
|
||||||
background-color: rgb(255, 255, 255) !important;
|
background-color: rgb(255, 255, 255) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-badge {
|
.v-badge {
|
||||||
&.x-small-badge {
|
&.x-small-badge {
|
||||||
.v-badge__badge {
|
.v-badge__badge {
|
||||||
height: 6px !important;
|
height: 6px !important;
|
||||||
width: 6px !important;
|
width: 6px !important;
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.one-line {
|
.one-line {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.two-line {
|
.two-line {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.z-1 {
|
.z-1 {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,48 @@
|
|||||||
.v-selection-control.v-selection-control--density-default {
|
.v-selection-control.v-selection-control--density-default {
|
||||||
.v-switch__track,
|
.v-switch__track,
|
||||||
.v-switch__thumb {
|
.v-switch__thumb {
|
||||||
background-color: rgb(var(--v-theme-grey200));
|
background-color: rgb(var(--v-theme-grey200));
|
||||||
|
}
|
||||||
|
&.v-selection-control--dirty {
|
||||||
|
.v-selection-control__wrapper.text-primary {
|
||||||
|
.v-switch__track {
|
||||||
|
background-color: rgba(var(--v-theme-primary), 0.6);
|
||||||
|
}
|
||||||
|
.v-switch__thumb {
|
||||||
|
background-color: rgb(var(--v-theme-primary));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.v-selection-control--dirty {
|
.v-selection-control__wrapper.text-secondary {
|
||||||
.v-selection-control__wrapper.text-primary {
|
.v-switch__track {
|
||||||
.v-switch__track {
|
background-color: rgba(var(--v-theme-secondary), 0.6);
|
||||||
background-color: rgba(var(--v-theme-primary), 0.6);
|
}
|
||||||
}
|
.v-switch__thumb {
|
||||||
.v-switch__thumb {
|
background-color: rgb(var(--v-theme-secondary));
|
||||||
background-color: rgb(var(--v-theme-primary));
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
.v-selection-control__wrapper.text-secondary {
|
|
||||||
.v-switch__track {
|
|
||||||
background-color: rgba(var(--v-theme-secondary), 0.6);
|
|
||||||
}
|
|
||||||
.v-switch__thumb {
|
|
||||||
background-color: rgb(var(--v-theme-secondary));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.v-selection-control__wrapper.text-warning {
|
|
||||||
.v-switch__track {
|
|
||||||
background-color: rgba(var(--v-theme-warning), 0.6);
|
|
||||||
}
|
|
||||||
.v-switch__thumb {
|
|
||||||
background-color: rgb(var(--v-theme-warning));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.v-selection-control__wrapper.text-error {
|
|
||||||
.v-switch__track {
|
|
||||||
background-color: rgba(var(--v-theme-error), 0.6);
|
|
||||||
}
|
|
||||||
.v-switch__thumb {
|
|
||||||
background-color: rgb(var(--v-theme-error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.v-selection-control__wrapper.text-success {
|
|
||||||
.v-switch__track {
|
|
||||||
background-color: rgba(var(--v-theme-success), 0.6);
|
|
||||||
}
|
|
||||||
.v-switch__thumb {
|
|
||||||
background-color: rgb(var(--v-theme-success));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.v-selection-control__wrapper.text-warning {
|
||||||
|
.v-switch__track {
|
||||||
|
background-color: rgba(var(--v-theme-warning), 0.6);
|
||||||
|
}
|
||||||
|
.v-switch__thumb {
|
||||||
|
background-color: rgb(var(--v-theme-warning));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.v-selection-control__wrapper.text-error {
|
||||||
|
.v-switch__track {
|
||||||
|
background-color: rgba(var(--v-theme-error), 0.6);
|
||||||
|
}
|
||||||
|
.v-switch__thumb {
|
||||||
|
background-color: rgb(var(--v-theme-error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.v-selection-control__wrapper.text-success {
|
||||||
|
.v-switch__track {
|
||||||
|
background-color: rgba(var(--v-theme-success), 0.6);
|
||||||
|
}
|
||||||
|
.v-switch__thumb {
|
||||||
|
background-color: rgb(var(--v-theme-success));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,81 +1,81 @@
|
|||||||
// theme : dark
|
// theme : dark
|
||||||
div[class*='v-theme--DARK_'] {
|
div[class*="v-theme--DARK_"] {
|
||||||
.smallCap {
|
.smallCap {
|
||||||
color: rgb(var(--v-theme-textSecondary));
|
color: rgb(var(--v-theme-textSecondary));
|
||||||
|
}
|
||||||
|
|
||||||
|
.elevation-10 {
|
||||||
|
box-shadow:
|
||||||
|
rgb(145 158 171 / 30%) 0px 0px 2px 0px,
|
||||||
|
rgb(145 158 171 / 2%) 0px 12px 24px -4px !important;
|
||||||
|
}
|
||||||
|
.v-field__outline {
|
||||||
|
--v-field-border-opacity: 0.38 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.front-wraper {
|
||||||
|
.bg-background {
|
||||||
|
background-color: rgb(var(--v-theme-hoverColor)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.elevation-10 {
|
.front-dark {
|
||||||
box-shadow: rgb(145 158 171 / 30%) 0px 0px 2px 0px, rgb(145 158 171 / 2%) 0px 12px 24px -4px !important;
|
&.bg-textPrimary {
|
||||||
|
background-color: rgb(var(--v-theme-surface)) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.v-field__outline{
|
.bg-textPrimary {
|
||||||
--v-field-border-opacity: 0.38 !important;
|
background-color: rgb(var(--v-theme-textSecondary)) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#vector-map .dxm-layers path {
|
||||||
|
fill: #7c8fac !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svgMap-map-wrapper {
|
||||||
|
.svgMap-country {
|
||||||
|
stroke: #878585;
|
||||||
|
fill: #1a2537 !important;
|
||||||
|
|
||||||
|
&#svgMap-map-country-IN {
|
||||||
|
fill: rgb(var(--v-theme-secondary)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&#svgMap-map-country-AF {
|
||||||
|
fill: rgb(var(--v-theme-purple)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&#svgMap-map-country-US {
|
||||||
|
fill: rgb(var(--v-theme-primary)) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.front-wraper{
|
.svgMap-map-controls-zoom {
|
||||||
.bg-background{
|
background: #c9d6de !important;
|
||||||
background-color: rgb(var(--v-theme-hoverColor)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.front-dark{
|
|
||||||
&.bg-textPrimary{
|
|
||||||
background-color: rgb(var(--v-theme-surface)) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.bg-textPrimary{
|
|
||||||
background-color: rgb(var(--v-theme-textSecondary)) !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.svgMap-control-button {
|
||||||
|
background-color: rgb(var(--v-theme-textSecondary)) !important;
|
||||||
#vector-map .dxm-layers path {
|
|
||||||
fill: #7C8FAC !important;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.dark-card-title {
|
||||||
|
color: rgb(var(--v-theme-surface));
|
||||||
|
}
|
||||||
|
|
||||||
.svgMap-map-wrapper {
|
.fc {
|
||||||
.svgMap-country {
|
.fc-button-primary:not(:disabled).fc-button-active {
|
||||||
stroke: #878585;
|
background-color: rgb(var(--v-theme-grey100));
|
||||||
fill: #1A2537 !important;
|
|
||||||
|
|
||||||
&#svgMap-map-country-IN {
|
|
||||||
fill: rgb(var(--v-theme-secondary)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&#svgMap-map-country-AF {
|
|
||||||
fill: rgb(var(--v-theme-purple)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&#svgMap-map-country-US {
|
|
||||||
fill: rgb(var(--v-theme-primary)) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.svgMap-map-controls-zoom {
|
|
||||||
background: #c9d6de !important;
|
|
||||||
}
|
|
||||||
.svgMap-control-button{
|
|
||||||
background-color: rgb(var(--v-theme-textSecondary)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.dark-card-title{
|
.fc-button-group {
|
||||||
color:rgb(var(--v-theme-surface));
|
> .fc-button {
|
||||||
}
|
&:hover,
|
||||||
|
&:focus {
|
||||||
.fc{
|
background-color: rgba(var(--v-theme-grey100));
|
||||||
.fc-button-primary:not(:disabled).fc-button-active {
|
color: #fff;
|
||||||
background-color: rgb(var(--v-theme-grey100));
|
.fc-icon {
|
||||||
}
|
color: #fff;
|
||||||
.fc-button-group {
|
}
|
||||||
>.fc-button {
|
|
||||||
&:hover,&:focus{
|
|
||||||
background-color: rgba(var(--v-theme-grey100));
|
|
||||||
color: #fff;
|
|
||||||
.fc-icon{
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,231 +1,222 @@
|
|||||||
@use "../variables" as *;
|
@use "../variables" as *;
|
||||||
|
|
||||||
.horizontalLayout {
|
.horizontalLayout {
|
||||||
.v-main {
|
.v-main {
|
||||||
margin: 0 16px !important;
|
margin: 0 16px !important;
|
||||||
|
|
||||||
@media screen and (max-width: 767px) {
|
@media screen and (max-width: 767px) {
|
||||||
margin: 0 10px !important;
|
margin: 0 10px !important;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.horizontal-header {
|
.horizontal-header {
|
||||||
&.v-app-bar .v-toolbar__content {
|
&.v-app-bar .v-toolbar__content {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maxWidth {
|
.maxWidth {
|
||||||
@media screen and (max-width: 1199px) {
|
@media screen and (max-width: 1199px) {
|
||||||
padding: 0 8px !important;
|
padding: 0 8px !important;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ddMenu {
|
.ddMenu {
|
||||||
&.ddLevel-1 {
|
&.ddLevel-1 {
|
||||||
.navItem {
|
.navItem {
|
||||||
.navItemLink {
|
.navItemLink {
|
||||||
.dot {
|
.dot {
|
||||||
height: 6px;
|
height: 6px;
|
||||||
width: 6px;
|
width: 6px;
|
||||||
background-color: rgb(var(--v-theme-textSecondary));
|
background-color: rgb(var(--v-theme-textSecondary));
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-inline-end: 8px !important;
|
margin-inline-end: 8px !important;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.dot {
|
|
||||||
background-color: rgb(var(--v-theme-secondary));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ddLevel-2 {
|
&:hover {
|
||||||
.navItem {
|
.dot {
|
||||||
.navItemLink {
|
background-color: rgb(var(--v-theme-secondary));
|
||||||
.dot {
|
|
||||||
height: 6px;
|
|
||||||
width: 6px;
|
|
||||||
background-color: rgb(var(--v-theme-textSecondary));
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-inline-end: 8px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.dot {
|
|
||||||
background-color: rgb(var(--v-theme-secondary));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ddLevel-2 {
|
||||||
|
.navItem {
|
||||||
|
.navItemLink {
|
||||||
|
.dot {
|
||||||
|
height: 6px;
|
||||||
|
width: 6px;
|
||||||
|
background-color: rgb(var(--v-theme-textSecondary));
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-inline-end: 8px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.dot {
|
||||||
|
background-color: rgb(var(--v-theme-secondary));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.horizontalMenu {
|
.horizontalMenu {
|
||||||
.v-toolbar__content {
|
.v-toolbar__content {
|
||||||
max-width: 1270px;
|
max-width: 1270px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navItem:has(.ddMenu.ddLevel-1 li a.router-link-active) {
|
||||||
|
background-color: rgb(var(--v-theme-secondary)) !important;
|
||||||
|
border-radius: 9999px;
|
||||||
|
|
||||||
|
.navcollapse {
|
||||||
|
color: rgba(255, 255, 255);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.navItem:has(.ddMenu.ddLevel-1 li a.router-link-active) {
|
|
||||||
background-color: rgb(var(--v-theme-secondary)) !important;
|
|
||||||
border-radius: 9999px;
|
|
||||||
|
|
||||||
.navcollapse {
|
|
||||||
color: rgba(255, 255, 255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-menu {
|
.mobile-menu {
|
||||||
.v-navigation-drawer {
|
.v-navigation-drawer {
|
||||||
margin-top: -70px !important;
|
margin-top: -70px !important;
|
||||||
height: 100vh !important;
|
height: 100vh !important;
|
||||||
z-index: 2000 !important;
|
z-index: 2000 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 960px) {
|
@media (min-width: 960px) {
|
||||||
.horizontalMenu {
|
.horizontalMenu {
|
||||||
margin-top: 65px;
|
margin-top: 65px;
|
||||||
margin-bottom: -70px;
|
margin-bottom: -70px;
|
||||||
|
|
||||||
.maxWidth {
|
.maxWidth {
|
||||||
.horizontal-navbar {
|
.horizontal-navbar {
|
||||||
max-width: 1160px;
|
max-width: 1160px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-navbar {
|
||||||
|
padding: 16px 0;
|
||||||
|
margin: 0px auto;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
z-index: 11;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.horizontal-navbar {
|
.ddMenu {
|
||||||
padding: 16px 0;
|
li {
|
||||||
margin: 0px auto;
|
a {
|
||||||
align-items: center;
|
color: rgb(var(--v-theme-textPrimary)) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
z-index: 11;
|
align-items: center;
|
||||||
font-size: 0.875rem;
|
padding: 10px 13px;
|
||||||
position: relative;
|
height: 40px;
|
||||||
|
|
||||||
ul {
|
.navIcon {
|
||||||
padding: 0px;
|
margin-right: 10px;
|
||||||
margin: 0px;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ddMenu {
|
.ddIcon {
|
||||||
li {
|
margin-top: 2px;
|
||||||
a {
|
|
||||||
color: rgb(var(--v-theme-textPrimary)) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
&.router-link-exact-active {
|
||||||
list-style: none;
|
background-color: transparent;
|
||||||
|
color: rgba(var(--v-theme-secondary)) !important;
|
||||||
|
|
||||||
a {
|
.dot {
|
||||||
text-decoration: none;
|
background-color: rgb(var(--v-theme-secondary)) !important;
|
||||||
display: flex;
|
}
|
||||||
align-items: center;
|
|
||||||
padding: 10px 13px;
|
|
||||||
height: 40px;
|
|
||||||
|
|
||||||
.navIcon {
|
|
||||||
margin-right: 10px;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ddIcon {
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.router-link-exact-active {
|
|
||||||
background-color: transparent;
|
|
||||||
color: rgba(var(--v-theme-secondary)) !important;
|
|
||||||
|
|
||||||
.dot {
|
|
||||||
background-color: rgb(var(--v-theme-secondary)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navItem {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.single-link {
|
|
||||||
&:hover {
|
|
||||||
color: rgb(var(--v-theme-secondary)) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.ddMenu {
|
|
||||||
.navItem {
|
|
||||||
.navcollapse {
|
|
||||||
&:hover {
|
|
||||||
color: rgb(var(--v-theme-secondary)) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.ddMenu {
|
|
||||||
position: absolute;
|
|
||||||
width: 230px;
|
|
||||||
display: none;
|
|
||||||
top: 40px;
|
|
||||||
padding: 10px;
|
|
||||||
z-index: 1;
|
|
||||||
background-color: rgb(var(--v-theme-surface));
|
|
||||||
box-shadow: $box-shadow;
|
|
||||||
border-radius: $border-radius-root;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ddLevel-2,
|
|
||||||
.ddLevel-3 {
|
|
||||||
top: -5px;
|
|
||||||
left: 212px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navItem:hover {
|
|
||||||
|
|
||||||
>.ddMenu {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
>li:hover {
|
|
||||||
background-color: rgb(var(--v-theme-lightprimary));
|
|
||||||
border-radius: 9999px;
|
|
||||||
|
|
||||||
>.navItemLink {
|
|
||||||
color: rgb(var(--v-theme-secondary));
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.router-link-exact-active {
|
|
||||||
color: rgb(var(--v-theme-secondary));
|
|
||||||
font-weight: 500;
|
|
||||||
background-color: rgb(var(--v-theme-lightprimary));
|
|
||||||
border-radius: $border-radius-root;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
.navItem {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.single-link {
|
||||||
|
&:hover {
|
||||||
|
color: rgb(var(--v-theme-secondary)) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ddMenu {
|
||||||
|
.navItem {
|
||||||
|
.navcollapse {
|
||||||
|
&:hover {
|
||||||
|
color: rgb(var(--v-theme-secondary)) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ddMenu {
|
||||||
|
position: absolute;
|
||||||
|
width: 230px;
|
||||||
|
display: none;
|
||||||
|
top: 40px;
|
||||||
|
padding: 10px;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: rgb(var(--v-theme-surface));
|
||||||
|
box-shadow: $box-shadow;
|
||||||
|
border-radius: $border-radius-root;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ddLevel-2,
|
||||||
|
.ddLevel-3 {
|
||||||
|
top: -5px;
|
||||||
|
left: 212px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navItem:hover {
|
||||||
|
> .ddMenu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> li:hover {
|
||||||
|
background-color: rgb(var(--v-theme-lightprimary));
|
||||||
|
border-radius: 9999px;
|
||||||
|
|
||||||
|
> .navItemLink {
|
||||||
|
color: rgb(var(--v-theme-secondary));
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.router-link-exact-active {
|
||||||
|
color: rgb(var(--v-theme-secondary));
|
||||||
|
font-weight: 500;
|
||||||
|
background-color: rgb(var(--v-theme-lightprimary));
|
||||||
|
border-radius: $border-radius-root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+308
-316
@@ -2,373 +2,365 @@
|
|||||||
|
|
||||||
/*This is for the logo*/
|
/*This is for the logo*/
|
||||||
.leftSidebar {
|
.leftSidebar {
|
||||||
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, .03), 0 0 1px 0 rgba(0, 0, 0, .1);
|
box-shadow:
|
||||||
|
0 3px 4px 0 rgba(0, 0, 0, 0.03),
|
||||||
|
0 0 1px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
padding-left: 7px;
|
padding-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-text {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile {
|
||||||
|
background: url("@/assets/images/backgrounds/user-info.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-name {
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
margin-top: -6px;
|
||||||
|
height: 35px;
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mini-icon {
|
.v-list--density-default .v-list-subheader {
|
||||||
display: none;
|
padding-inline-start: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mini-text {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile {
|
|
||||||
background: url("@/assets/images/backgrounds/user-info.jpg") no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-name {
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
margin-top: -6px;
|
|
||||||
height: 35px;
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-list--density-default .v-list-subheader {
|
|
||||||
padding-inline-start: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.verticalLayout {
|
.verticalLayout {
|
||||||
.logo {
|
.logo {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
|
|
||||||
@media screen and (max-width:1024px) {
|
@media screen and (max-width: 1024px) {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*This is for the Vertical sidebar*/
|
/*This is for the Vertical sidebar*/
|
||||||
.scrollnavbar {
|
.scrollnavbar {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.userbottom {
|
.userbottom {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallCap {
|
||||||
|
padding: 3px 12px 12px 0px !important;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-top: 24px;
|
||||||
|
color: rgb(var(--v-theme-textPrimary));
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*General Menu css*/
|
||||||
|
.v-list-group__items .v-list-item,
|
||||||
|
.v-list-item {
|
||||||
|
border-radius: $border-radius-root;
|
||||||
|
padding-inline-start: calc(14px + var(--indent-padding) / 10) !important;
|
||||||
|
|
||||||
|
margin: 0 0 2px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgb(var(--v-theme-secondary));
|
||||||
}
|
}
|
||||||
|
|
||||||
.smallCap {
|
.v-list-item__prepend {
|
||||||
padding: 3px 12px 12px 0px !important;
|
margin-inline-end: 13px;
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-top: 24px;
|
|
||||||
color: rgb(var(--v-theme-textPrimary));
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-top: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.v-list-item__append {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
|
||||||
|
.v-icon {
|
||||||
|
margin-inline-start: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*General Menu css*/
|
.v-list-item-title {
|
||||||
.v-list-group__items .v-list-item,
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-list-group__items {
|
||||||
.v-list-item {
|
.v-list-item {
|
||||||
border-radius: $border-radius-root;
|
min-height: 35px !important;
|
||||||
padding-inline-start: calc(14px + var(--indent-padding) / 10) !important;
|
padding-inline-start: calc(12px + var(--indent-padding) / 10) !important;
|
||||||
|
|
||||||
margin: 0 0 2px;
|
.v-list-item__prepend .dot {
|
||||||
|
height: 6px;
|
||||||
|
width: 6px;
|
||||||
|
background-color: rgb(var(--v-theme-textSecondary));
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-inline-end: 8px !important;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-list-item-title {
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgb(var(--v-theme-secondary));
|
||||||
|
|
||||||
&:hover {
|
.v-list-item__prepend .dot {
|
||||||
color: rgb(var(--v-theme-secondary));
|
background-color: rgb(var(--v-theme-secondary));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.v-list-item--active {
|
||||||
|
.v-list-item__prepend .dot {
|
||||||
.v-list-item__prepend {
|
background-color: rgb(var(--v-theme-secondary));
|
||||||
margin-inline-end: 13px;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.v-list-item__append {
|
/*This is for the dropdown*/
|
||||||
font-size: 0.875rem;
|
.v-list {
|
||||||
|
color: rgb(var(--v-theme-textPrimary));
|
||||||
|
|
||||||
.v-icon {
|
> .v-list-item.v-list-item--active,
|
||||||
margin-inline-start: 13px;
|
.v-list-item--active > .v-list-item__overlay {
|
||||||
}
|
background: rgb(var(--v-theme-secondary));
|
||||||
}
|
color: white;
|
||||||
|
|
||||||
.v-list-item-title {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-list-group__items {
|
> .v-list-group {
|
||||||
.v-list-item {
|
position: relative;
|
||||||
min-height: 35px !important;
|
|
||||||
padding-inline-start: calc(12px + var(--indent-padding) / 10) !important;
|
|
||||||
|
|
||||||
.v-list-item__prepend .dot {
|
> .v-list-item--active,
|
||||||
height: 6px;
|
> .v-list-item--active:hover {
|
||||||
width: 6px;
|
background: rgb(var(--v-theme-secondary));
|
||||||
background-color: rgb(var(--v-theme-textSecondary));
|
color: white;
|
||||||
border-radius: 50%;
|
}
|
||||||
margin-inline-end: 8px !important;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-list-item-title {
|
.v-list-group__items .v-list-item.v-list-item--active,
|
||||||
font-size: 14px !important;
|
.v-list-group__items .v-list-item.v-list-item--active > .v-list-item__overlay {
|
||||||
}
|
background: transparent;
|
||||||
|
color: rgb(var(--v-theme-secondary));
|
||||||
&:hover {
|
}
|
||||||
color: rgb(var(--v-theme-secondary));
|
|
||||||
|
|
||||||
.v-list-item__prepend .dot {
|
|
||||||
background-color: rgb(var(--v-theme-secondary));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.v-list-item--active {
|
|
||||||
.v-list-item__prepend .dot {
|
|
||||||
background-color: rgb(var(--v-theme-secondary));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*This is for the dropdown*/
|
|
||||||
.v-list {
|
|
||||||
color: rgb(var(--v-theme-textPrimary));
|
|
||||||
|
|
||||||
>.v-list-item.v-list-item--active,
|
|
||||||
.v-list-item--active>.v-list-item__overlay {
|
|
||||||
background: rgb(var(--v-theme-secondary));
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
>.v-list-group {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
>.v-list-item--active,
|
|
||||||
>.v-list-item--active:hover {
|
|
||||||
background: rgb(var(--v-theme-secondary));
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-list-group__items .v-list-item.v-list-item--active,
|
|
||||||
.v-list-group__items .v-list-item.v-list-item--active>.v-list-item__overlay {
|
|
||||||
background: transparent;
|
|
||||||
color: rgb(var(--v-theme-secondary));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-navigation-drawer--rail {
|
.v-navigation-drawer--rail {
|
||||||
|
.scrollnavbar .v-list .v-list-group__items,
|
||||||
|
.hide-menu {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.scrollnavbar .v-list .v-list-group__items,
|
.leftPadding {
|
||||||
.hide-menu {
|
margin-left: 0px;
|
||||||
opacity: 1;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.leftPadding {
|
|
||||||
margin-left: 0px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: 1170px) {
|
@media only screen and (min-width: 1170px) {
|
||||||
.mini-sidebar {
|
.mini-sidebar {
|
||||||
.logo {
|
.logo {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.profile-logout {
|
|
||||||
opacity: 0;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollnavbar {
|
|
||||||
.smallCap {
|
|
||||||
padding: 3px 12px 12px 12px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftSidebar .v-list--density-default .v-list-subheader {
|
|
||||||
padding-inline-start: 15px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mini-icon {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebarchip.hide-menu {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mini-text {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-list {
|
|
||||||
padding: 14px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-list-group__items {
|
|
||||||
.iconClass {
|
|
||||||
position: relative;
|
|
||||||
left: -2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftSidebar:hover {
|
|
||||||
box-shadow: $box-shadow !important;
|
|
||||||
|
|
||||||
.mini-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebarchip.hide-menu {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mini-text {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-logout {
|
|
||||||
opacity: 1;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollnavbar {
|
|
||||||
.smallCap {
|
|
||||||
padding: 3px 12px 12px 0px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-list-group__items {
|
|
||||||
.iconClass {
|
|
||||||
position: relative;
|
|
||||||
left: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-list--density-default .v-list-subheader {
|
|
||||||
padding-inline-start: 0px !important;
|
|
||||||
}
|
|
||||||
.v-list-group__items {
|
|
||||||
.v-list-item {
|
|
||||||
.v-list-item__prepend .dot {
|
|
||||||
opacity:0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-navigation-drawer--expand-on-hover:hover {
|
|
||||||
.logo {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-list .v-list-group__items,
|
|
||||||
.hide-menu {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-img {
|
|
||||||
margin-left: 0;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
left: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-toggle {
|
|
||||||
margin-left: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-list-group__items {
|
|
||||||
.v-list-item {
|
|
||||||
.v-list-item__prepend .dot {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.profile-logout {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollnavbar {
|
||||||
|
.smallCap {
|
||||||
|
padding: 3px 12px 12px 12px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.leftSidebar .v-list--density-default .v-list-subheader {
|
||||||
|
padding-inline-start: 15px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-icon {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarchip.hide-menu {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-list {
|
||||||
|
padding: 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-list-group__items {
|
||||||
|
.iconClass {
|
||||||
|
position: relative;
|
||||||
|
left: -2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.leftSidebar:hover {
|
||||||
|
box-shadow: $box-shadow !important;
|
||||||
|
|
||||||
|
.mini-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarchip.hide-menu {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-text {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-logout {
|
||||||
|
opacity: 1;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollnavbar {
|
||||||
|
.smallCap {
|
||||||
|
padding: 3px 12px 12px 0px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-list-group__items {
|
||||||
|
.iconClass {
|
||||||
|
position: relative;
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-list--density-default .v-list-subheader {
|
||||||
|
padding-inline-start: 0px !important;
|
||||||
|
}
|
||||||
|
.v-list-group__items {
|
||||||
|
.v-list-item {
|
||||||
|
.v-list-item__prepend .dot {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-navigation-drawer--expand-on-hover:hover {
|
||||||
|
.logo {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-list .v-list-group__items,
|
||||||
|
.hide-menu {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-img {
|
||||||
|
margin-left: 0;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-toggle {
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-list-group__items {
|
||||||
|
.v-list-item {
|
||||||
|
.v-list-item__prepend .dot {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// scrollbar
|
// scrollbar
|
||||||
.ps__rail-y {
|
.ps__rail-y {
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-img {
|
.profile-img {
|
||||||
margin-left: 14px;
|
margin-left: 14px;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
-webkit-animation: 2.5s blow 0s linear infinite;
|
-webkit-animation: 2.5s blow 0s linear infinite;
|
||||||
animation: 2.5s blow 0s linear infinite;
|
animation: 2.5s blow 0s linear infinite;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: "";
|
content: "";
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
top: 40px;
|
top: 40px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
left: 26px;
|
left: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes blow {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.1);
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: scale3d(1, 1, 0.5);
|
||||||
|
transform: scale3d(1, 1, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes blow {
|
50% {
|
||||||
0% {
|
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.1);
|
opacity: 1;
|
||||||
opacity: 1;
|
-webkit-transform: scale3d(1, 1, 0.5);
|
||||||
-webkit-transform: scale3d(1, 1, 0.5);
|
transform: scale3d(1, 1, 0.5);
|
||||||
transform: scale3d(1, 1, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
50% {
|
|
||||||
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
opacity: 1;
|
|
||||||
-webkit-transform: scale3d(1, 1, 0.5);
|
|
||||||
transform: scale3d(1, 1, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
box-shadow: 0 0 0 20px rgba(0, 0, 0, 0.1);
|
|
||||||
opacity: 0;
|
|
||||||
-webkit-transform: scale3d(1, 1, 0.5);
|
|
||||||
transform: scale3d(1, 1, 0.5);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes blow {
|
100% {
|
||||||
0% {
|
box-shadow: 0 0 0 20px rgba(0, 0, 0, 0.1);
|
||||||
box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.1);
|
opacity: 0;
|
||||||
opacity: 1;
|
-webkit-transform: scale3d(1, 1, 0.5);
|
||||||
-webkit-transform: scale3d(1, 1, 0.5);
|
transform: scale3d(1, 1, 0.5);
|
||||||
transform: scale3d(1, 1, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
50% {
|
|
||||||
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
opacity: 1;
|
|
||||||
-webkit-transform: scale3d(1, 1, 0.5);
|
|
||||||
transform: scale3d(1, 1, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
box-shadow: 0 0 0 20px rgba(0, 0, 0, 0.1);
|
|
||||||
opacity: 0;
|
|
||||||
-webkit-transform: scale3d(1, 1, 0.5);
|
|
||||||
transform: scale3d(1, 1, 0.5);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes blow {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.1);
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: scale3d(1, 1, 0.5);
|
||||||
|
transform: scale3d(1, 1, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: scale3d(1, 1, 0.5);
|
||||||
|
transform: scale3d(1, 1, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 20px rgba(0, 0, 0, 0.1);
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: scale3d(1, 1, 0.5);
|
||||||
|
transform: scale3d(1, 1, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+183
-183
@@ -1,240 +1,240 @@
|
|||||||
//
|
//
|
||||||
// common
|
// common
|
||||||
//
|
//
|
||||||
|
|
||||||
.inside-left-sidebar {
|
.inside-left-sidebar {
|
||||||
.left-part {
|
.left-part {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
//Full Calendar
|
//Full Calendar
|
||||||
.fc {
|
.fc {
|
||||||
.fc-button-group {
|
.fc-button-group {
|
||||||
>.fc-button {
|
> .fc-button {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 7px 10px;
|
padding: 7px 10px;
|
||||||
border: 0;
|
border: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
background-color: rgba(var(--v-theme-grey200));
|
background-color: rgba(var(--v-theme-grey200));
|
||||||
color: rgba(var(--v-theme-textPrimary));
|
color: rgba(var(--v-theme-textPrimary));
|
||||||
.fc-icon{
|
.fc-icon {
|
||||||
color: rgba(var(--v-theme-textPrimary));
|
color: rgba(var(--v-theme-textPrimary));
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
&:hover,&:focus{
|
&:hover,
|
||||||
background-color: rgba(var(--v-theme-textPrimary));
|
&:focus {
|
||||||
color: #fff;
|
background-color: rgba(var(--v-theme-textPrimary));
|
||||||
.fc-icon{
|
color: #fff;
|
||||||
color: #fff;
|
.fc-icon {
|
||||||
}
|
color: #fff;
|
||||||
}
|
|
||||||
}
|
|
||||||
.fc-button-primary:not(:disabled):active{
|
|
||||||
background-color: rgba(var(--v-theme-textPrimary));
|
|
||||||
}
|
|
||||||
.fc-button-primary:not(:disabled).fc-button-active {
|
|
||||||
background-color: rgb(var(--v-theme-textPrimary));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fc-today-button{
|
}
|
||||||
padding: 7px 20px;
|
.fc-button-primary:not(:disabled):active {
|
||||||
border: 0;
|
background-color: rgba(var(--v-theme-textPrimary));
|
||||||
background-color: rgba(var(--v-theme-primary));
|
}
|
||||||
}
|
.fc-button-primary:not(:disabled).fc-button-active {
|
||||||
|
background-color: rgb(var(--v-theme-textPrimary));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.fc-today-button {
|
||||||
|
padding: 7px 20px;
|
||||||
|
border: 0;
|
||||||
|
background-color: rgba(var(--v-theme-primary));
|
||||||
|
}
|
||||||
|
|
||||||
.fc-prev-button{
|
.fc-prev-button {
|
||||||
border-radius: 30px 0 0 30px;
|
border-radius: 30px 0 0 30px;
|
||||||
}
|
}
|
||||||
.fc-toolbar-title{
|
.fc-toolbar-title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.fc-event-title{
|
.fc-event-title {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 3px 6px;
|
padding: 3px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-button {
|
.fc-button {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
|
|
||||||
.fc-icon {
|
.fc-icon {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
vertical-align: unset;
|
vertical-align: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fc-daygrid-day-number{
|
.fc-daygrid-day-number {
|
||||||
color: rgba(var(--v-theme-textSecondary));
|
color: rgba(var(--v-theme-textSecondary));
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-button-primary {
|
.fc-button-primary {
|
||||||
background: rgb(var(--v-theme-primary));
|
background: rgb(var(--v-theme-primary));
|
||||||
border-color: rgb(var(--v-theme-primary));
|
border-color: rgb(var(--v-theme-primary));
|
||||||
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgb(var(--v-theme-primary));
|
background-color: rgb(var(--v-theme-primary));
|
||||||
border-color: rgb(var(--v-theme-primary));
|
border-color: rgb(var(--v-theme-primary));
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:disabled).fc-button-active {
|
&:not(:disabled).fc-button-active {
|
||||||
background-color: rgb(var(--v-theme-primary));
|
background-color: rgb(var(--v-theme-primary));
|
||||||
border-color: rgb(var(--v-theme-primary));
|
border-color: rgb(var(--v-theme-primary));
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:disabled) {
|
&:not(:disabled) {
|
||||||
&:active {
|
&:active {
|
||||||
background-color: rgb(var(--v-theme-primary));
|
background-color: rgb(var(--v-theme-primary));
|
||||||
border-color: rgb(var(--v-theme-primary));
|
border-color: rgb(var(--v-theme-primary));
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
background-color: rgb(var(--v-theme-primary));
|
background-color: rgb(var(--v-theme-primary));
|
||||||
border-color: rgb(var(--v-theme-primary));
|
border-color: rgb(var(--v-theme-primary));
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-col-header-cell-cushion {
|
.fc-col-header-cell-cushion {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 10px 5px;
|
padding: 10px 5px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.fc-button-primary:not(:disabled).fc-button-active{
|
.fc-button-primary:not(:disabled).fc-button-active {
|
||||||
background-color: rgb(var(--v-theme-textPrimary));
|
background-color: rgb(var(--v-theme-textPrimary));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-theme-standard {
|
.fc-theme-standard {
|
||||||
td {
|
td {
|
||||||
border: 1px solid rgba(var(--v-border-color), 1) !important;
|
border: 1px solid rgba(var(--v-border-color), 1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
border: 1px solid rgba(var(--v-border-color), 1) !important;
|
border: 1px solid rgba(var(--v-border-color), 1) !important;
|
||||||
border-bottom: 0 !important;
|
border-bottom: 0 !important;
|
||||||
background-color: rgba(var(--v-theme-grey200));
|
background-color: rgba(var(--v-theme-grey200));
|
||||||
height: 56px;
|
height: 56px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
color: rgba(var(--v-theme-textSecondary));
|
color: rgba(var(--v-theme-textSecondary));
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-scrollgrid {
|
.fc-scrollgrid {
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-h-event {
|
.fc-h-event {
|
||||||
background-color: rgb(var(--v-theme-primary));
|
background-color: rgb(var(--v-theme-primary));
|
||||||
border: 0;
|
border: 0;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-direction-ltr {
|
.fc-direction-ltr {
|
||||||
.fc-button-group {
|
.fc-button-group {
|
||||||
>.fc-button {
|
> .fc-button {
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
border-bottom-left-radius: 30px;
|
border-bottom-left-radius: 30px;
|
||||||
border-top-left-radius: 30px;
|
border-top-left-radius: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:first-child) {
|
&:not(:first-child) {
|
||||||
border-bottom-right-radius: 30px;
|
border-bottom-right-radius: 30px;
|
||||||
border-top-right-radius: 30px;
|
border-top-right-radius: 30px;
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-button-group {
|
.fc-button-group {
|
||||||
.fc-dayGridMonth-button {
|
.fc-dayGridMonth-button {
|
||||||
border-bottom-right-radius: 0px !important;
|
border-bottom-right-radius: 0px !important;
|
||||||
border-top-right-radius: 0px !important;
|
border-top-right-radius: 0px !important;
|
||||||
padding: 7px 20px !important;
|
padding: 7px 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-timeGridDay-button {
|
.fc-timeGridDay-button {
|
||||||
border-bottom-left-radius: 0px !important;
|
border-bottom-left-radius: 0px !important;
|
||||||
border-top-left-radius: 0px !important;
|
border-top-left-radius: 0px !important;
|
||||||
padding: 7px 20px !important;
|
padding: 7px 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-timeGridWeek-button {
|
.fc-timeGridWeek-button {
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
padding: 7px 20px !important;
|
padding: 7px 20px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-today-button {
|
.fc-today-button {
|
||||||
border-radius: 30px !important;
|
border-radius: 30px !important;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width:600px) {
|
@media screen and (max-width: 600px) {
|
||||||
.fc {
|
.fc {
|
||||||
.fc-toolbar {
|
.fc-toolbar {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-toolbar-chunk {
|
.fc-toolbar-chunk {
|
||||||
.fc-toolbar-title {
|
.fc-toolbar-title {
|
||||||
margin: 15px 0;
|
margin: 15px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.customTab {
|
.customTab {
|
||||||
.v-btn {
|
.v-btn {
|
||||||
&.v-tab-item--selected {
|
&.v-tab-item--selected {
|
||||||
background-color: rgb(var(--v-theme-lightprimary)) !important;
|
background-color: rgb(var(--v-theme-lightprimary)) !important;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
background-color: rgb(var(--v-theme-primary)) !important;
|
background-color: rgb(var(--v-theme-primary)) !important;
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.email-items {
|
.email-items {
|
||||||
padding: 0px 24px 12px;
|
padding: 0px 24px 12px;
|
||||||
|
|
||||||
&.selected-email {
|
&.selected-email {
|
||||||
.email-title {
|
.email-title {
|
||||||
color: rgb(var(--v-theme-primary)) !important;
|
color: rgb(var(--v-theme-primary)) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgb(var(--v-theme-hoverColor));
|
background-color: rgb(var(--v-theme-hoverColor));
|
||||||
|
|
||||||
.email-title {
|
.email-title {
|
||||||
color: rgb(var(--v-theme-primary)) !important;
|
color: rgb(var(--v-theme-primary)) !important;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.email-content {
|
.email-content {
|
||||||
p {
|
p {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,59 @@
|
|||||||
// Light Theme Colors
|
// Light Theme Colors
|
||||||
.v-theme--AQUA_THEME {
|
.v-theme--AQUA_THEME {
|
||||||
--v-theme-primary: 0, 116, 186;
|
--v-theme-primary: 0, 116, 186;
|
||||||
--v-theme-secondary: 71, 215, 188;
|
--v-theme-secondary: 71, 215, 188;
|
||||||
--v-theme-lightprimary: 229, 241, 248;
|
--v-theme-lightprimary: 229, 241, 248;
|
||||||
--v-theme-lightsecondary: 237, 251, 247;
|
--v-theme-lightsecondary: 237, 251, 247;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-theme--PURPLE_THEME {
|
.v-theme--PURPLE_THEME {
|
||||||
--v-theme-primary: 118, 62, 189;
|
--v-theme-primary: 118, 62, 189;
|
||||||
--v-theme-secondary: 148, 208, 214;
|
--v-theme-secondary: 148, 208, 214;
|
||||||
--v-theme-lightprimary: 242, 236, 249;
|
--v-theme-lightprimary: 242, 236, 249;
|
||||||
--v-theme-lightsecondary: 237, 248, 250;
|
--v-theme-lightsecondary: 237, 248, 250;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-theme--GREEN_THEME {
|
.v-theme--GREEN_THEME {
|
||||||
--v-theme-primary: 10, 126, 164;
|
--v-theme-primary: 10, 126, 164;
|
||||||
--v-theme-secondary: 204, 218, 78;
|
--v-theme-secondary: 204, 218, 78;
|
||||||
--v-theme-lightprimary: 230, 242, 246;
|
--v-theme-lightprimary: 230, 242, 246;
|
||||||
--v-theme-lightsecondary: 250, 251, 239;
|
--v-theme-lightsecondary: 250, 251, 239;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-theme--CYAN_THEME {
|
.v-theme--CYAN_THEME {
|
||||||
--v-theme-primary: 1, 192, 200;
|
--v-theme-primary: 1, 192, 200;
|
||||||
--v-theme-secondary: 251, 150, 120;
|
--v-theme-secondary: 251, 150, 120;
|
||||||
--v-theme-lightprimary: 235, 249, 250;
|
--v-theme-lightprimary: 235, 249, 250;
|
||||||
--v-theme-lightsecondary: 255, 245, 242;
|
--v-theme-lightsecondary: 255, 245, 242;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-theme--ORANGE_THEME {
|
.v-theme--ORANGE_THEME {
|
||||||
--v-theme-primary: 250, 137, 107;
|
--v-theme-primary: 250, 137, 107;
|
||||||
--v-theme-secondary: 0, 116, 186;
|
--v-theme-secondary: 0, 116, 186;
|
||||||
--v-theme-lightprimary: 251, 242, 239;
|
--v-theme-lightprimary: 251, 242, 239;
|
||||||
--v-theme-lightsecondary: 239, 249, 255;
|
--v-theme-lightsecondary: 239, 249, 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-theme--DARK_AQUA_THEME{
|
.v-theme--DARK_AQUA_THEME {
|
||||||
--v-theme-primary: 0, 116, 186;
|
--v-theme-primary: 0, 116, 186;
|
||||||
--v-theme-secondary: 71, 215, 188;
|
--v-theme-secondary: 71, 215, 188;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-theme--DARK_PURPLE_THEME {
|
.v-theme--DARK_PURPLE_THEME {
|
||||||
--v-theme-primary: 118, 62, 189;
|
--v-theme-primary: 118, 62, 189;
|
||||||
--v-theme-secondary: 148, 208, 214;
|
--v-theme-secondary: 148, 208, 214;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-theme--DARK_GREEN_THEME {
|
.v-theme--DARK_GREEN_THEME {
|
||||||
--v-theme-primary: 10, 126, 164;
|
--v-theme-primary: 10, 126, 164;
|
||||||
--v-theme-secondary: 204, 218, 78;
|
--v-theme-secondary: 204, 218, 78;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-theme--DARK_CYAN_THEME {
|
.v-theme--DARK_CYAN_THEME {
|
||||||
--v-theme-primary: 1, 192, 200;
|
--v-theme-primary: 1, 192, 200;
|
||||||
--v-theme-secondary: 251, 150, 120;
|
--v-theme-secondary: 251, 150, 120;
|
||||||
}
|
}
|
||||||
.v-theme--DARK_ORANGE_THEME {
|
.v-theme--DARK_ORANGE_THEME {
|
||||||
--v-theme-primary: 250, 137, 107;
|
--v-theme-primary: 250, 137, 107;
|
||||||
--v-theme-secondary: 0, 116, 186;
|
--v-theme-secondary: 0, 116, 186;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,8 +189,8 @@ const CHIRPY_JEKYLL_DARK_THEME: ThemeTypes = {
|
|||||||
"border-opacity": 1,
|
"border-opacity": 1,
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
primary: "#8ab4f8",
|
primary: "#07a8f7",
|
||||||
secondary: "#40b883",
|
secondary: "#8ab4f8",
|
||||||
info: "#0075d1",
|
info: "#0075d1",
|
||||||
success: "#0fa30f",
|
success: "#0fa30f",
|
||||||
accent: "#8ab4f8",
|
accent: "#8ab4f8",
|
||||||
@@ -199,7 +199,7 @@ const CHIRPY_JEKYLL_DARK_THEME: ThemeTypes = {
|
|||||||
purple: "#b370f5",
|
purple: "#b370f5",
|
||||||
indigo: "#7c6af0",
|
indigo: "#7c6af0",
|
||||||
lightprimary: "#1a2540",
|
lightprimary: "#1a2540",
|
||||||
lightsecondary: "#0d2e20",
|
lightsecondary: "#072a3a",
|
||||||
lightsuccess: "#163c24",
|
lightsuccess: "#163c24",
|
||||||
lighterror: "#3a0000",
|
lighterror: "#3a0000",
|
||||||
lightwarning: "#3a2a10",
|
lightwarning: "#3a2a10",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import markedKatex from "marked-katex-extension"
|
|||||||
import "katex/dist/katex.min.css"
|
import "katex/dist/katex.min.css"
|
||||||
|
|
||||||
marked.use(markedKatex({ throwOnError: false }))
|
marked.use(markedKatex({ throwOnError: false }))
|
||||||
|
marked.use({ breaks: true })
|
||||||
|
|
||||||
export function renderMarkdown(source: string): string {
|
export function renderMarkdown(source: string): string {
|
||||||
return DOMPurify.sanitize(marked.parse(source) as string)
|
return DOMPurify.sanitize(marked.parse(source) as string)
|
||||||
|
|||||||
Reference in New Issue
Block a user