mirror of
https://forgejo.merr.is/annika/isl-vue3.git
synced 2025-12-19 05:46:33 -05:00
Compare commits
No commits in common. "main" and "v0.1.27" have entirely different histories.
33 changed files with 293 additions and 1073 deletions
|
|
@ -58,20 +58,20 @@ jobs:
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
# - name: Extract Binary For Release
|
- name: Extract Binary For Release
|
||||||
# uses: https://github.com/moosetheory/actions-docker-extract@v3.1
|
uses: https://github.com/moosetheory/actions-docker-extract@v3.1
|
||||||
# id: extract
|
id: extract
|
||||||
# if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
# with:
|
with:
|
||||||
# shell_command: /bin/ash -c
|
shell_command: /bin/ash -c
|
||||||
# image: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}
|
image: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}
|
||||||
# path: /usr/share/nginx/html
|
path: /dist
|
||||||
# destination: dist
|
destination: dist
|
||||||
# - name: Create a Release
|
- name: Create a Release
|
||||||
# uses: https://forgejo.merr.is/actions/forgejo-release@alpinev1
|
uses: https://forgejo.merr.is/actions/forgejo-release@alpinev1
|
||||||
# if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
# with:
|
with:
|
||||||
# direction: upload
|
direction: upload
|
||||||
# release-dir: dist
|
release-dir: dist
|
||||||
# token: ${{ secrets.ADMIN_TOKEN }}
|
token: ${{ secrets.ADMIN_TOKEN }}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,6 @@ COPY . .
|
||||||
|
|
||||||
# [optional] tests & build
|
# [optional] tests & build
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
# I can't run the type checking because of some sort of issue with
|
|
||||||
# bun and vue-tsc.
|
|
||||||
# see https://github.com/oven-sh/bun/issues/4754
|
|
||||||
# RUN bunx --bun vue-tsc --build --force
|
|
||||||
RUN bunx --bun vite build
|
RUN bunx --bun vite build
|
||||||
|
|
||||||
# Copy the distribution folder into the final image.
|
# Copy the distribution folder into the final image.
|
||||||
|
|
|
||||||
31
Dockerfile.dev
Normal file
31
Dockerfile.dev
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# This is making minimal changes from the guide at
|
||||||
|
# https://bun.sh/guides/ecosystem/docker (as of 2024-02-04)
|
||||||
|
|
||||||
|
# use the official Bun image
|
||||||
|
# see all versions at https://hub.docker.com/r/oven/bun/tags
|
||||||
|
FROM oven/bun:1-alpine as base
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# install dependencies into temp directory
|
||||||
|
# this will cache them and speed up future builds
|
||||||
|
FROM base as install
|
||||||
|
RUN mkdir -p /tmp/dev
|
||||||
|
COPY package.json bun.lockb /temp/dev/
|
||||||
|
RUN cd /temp/dev && bun install --frozen-lockfile
|
||||||
|
|
||||||
|
# install with --production (exclude devDependencies)
|
||||||
|
RUN mkdir -p /temp/prod
|
||||||
|
COPY package.json bun.lockb /temp/prod/
|
||||||
|
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
||||||
|
# ^ Is this necessary? Don't we either need prod or not prod?
|
||||||
|
|
||||||
|
# copy node_modules from temp directory
|
||||||
|
# then copy all (non-ignored) project files into the image
|
||||||
|
FROM base AS prerelease
|
||||||
|
COPY --from=install /temp/dev/node_modules node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
FROM nginx:stable-alpine as release
|
||||||
|
|
||||||
|
COPY --from=build-stage /app/docker/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||||
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
|
@ -15,11 +15,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.5",
|
"axios": "^1.6.5",
|
||||||
"axios-retry": "^4.0.0",
|
|
||||||
"oidc-client-ts": "^3.0.1",
|
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
|
"sass": "^1.69.7",
|
||||||
"vue": "^3.3.11",
|
"vue": "^3.3.11",
|
||||||
"vue-oidc-client": "^1.0.0-alpha.5",
|
|
||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.2.5",
|
||||||
"vuetify": "^3.4.10"
|
"vuetify": "^3.4.10"
|
||||||
},
|
},
|
||||||
|
|
@ -43,8 +41,6 @@
|
||||||
"typescript": "~5.3.0",
|
"typescript": "~5.3.0",
|
||||||
"vite": "^5.0.10",
|
"vite": "^5.0.10",
|
||||||
"vitest": "^1.0.4",
|
"vitest": "^1.0.4",
|
||||||
"vue-tsc": "^1.8.25",
|
"vue-tsc": "^1.8.25"
|
||||||
"sass": "^1.69.7",
|
|
||||||
"vite-plugin-vuetify": "^2.0.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"apiBaseURL": "http://coder.local.merr.is:3001",
|
|
||||||
"oidcAuthority": "https://auth.joes.moosenet.work",
|
|
||||||
"oidcClientID": "255988227184328707@isekai:_slow_life_calculator",
|
|
||||||
"oidcProjectID": "255987963094106115"
|
|
||||||
}
|
|
||||||
14
src/App.vue
14
src/App.vue
|
|
@ -1,13 +1,25 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { RouterView } from 'vue-router'
|
import { RouterView } from 'vue-router'
|
||||||
import GlobalHeader from '@/components/GlobalHeader.vue'
|
import GlobalHeader from '@/components/GlobalHeader.vue'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { usePowerItems } from './stores/powerItems'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const { blessingPowerItems, fellowPowerItems, intimacyPowerItems } = storeToRefs(usePowerItems())
|
||||||
|
|
||||||
|
const isLoaded = computed(
|
||||||
|
() =>
|
||||||
|
!blessingPowerItems.value.keys().next().done &&
|
||||||
|
!fellowPowerItems.value.keys().next().done &&
|
||||||
|
!intimacyPowerItems.value.keys().next().done
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-app>
|
<v-app>
|
||||||
<GlobalHeader />
|
<GlobalHeader />
|
||||||
<v-main class="d-flex">
|
<v-main class="d-flex">
|
||||||
<RouterView />
|
<RouterView v-if="isLoaded" />
|
||||||
</v-main>
|
</v-main>
|
||||||
<v-footer app>
|
<v-footer app>
|
||||||
<div>Copyright Annika Merris 2024</div>
|
<div>Copyright Annika Merris 2024</div>
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { oidc } from '@/types/ConfigSymbols';
|
import { ref } from 'vue';
|
||||||
import { inject } from 'vue';
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
|
|
||||||
const zitadel = ref(inject(oidc))
|
|
||||||
const route = ref(useRoute())
|
|
||||||
zitadel.value?.isAuthenticated
|
|
||||||
const drawer = ref(false)
|
const drawer = ref(false)
|
||||||
|
|
||||||
const login = () => {
|
|
||||||
console.log(route.value.path)
|
|
||||||
zitadel.value?.signIn({redirect_uri: "http://coder.local.merr.is:5173/login"})
|
|
||||||
}
|
|
||||||
const logout = () => {
|
|
||||||
zitadel.value?.signOut()
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -40,11 +25,5 @@ const logout = () => {
|
||||||
<v-list-item-title>Intimacy Power</v-list-item-title>
|
<v-list-item-title>Intimacy Power</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
<template v-slot:append>
|
|
||||||
<div class="pa-2">
|
|
||||||
<v-btn block @click="logout" v-if="zitadel?.isAuthenticated">Logout</v-btn>
|
|
||||||
<v-btn block @click="login" v-else>Login</v-btn>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,213 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import type { PowerItem } from '@/types/PowerItem'
|
|
||||||
import { getCurrentInstance } from 'vue'
|
|
||||||
import {User} from "oidc-client";
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
type EventItemListing = {
|
|
||||||
name: string
|
|
||||||
id: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
|
|
||||||
const itemType = ref(0)
|
|
||||||
const itemName = ref('')
|
|
||||||
const iconURL = ref('')
|
|
||||||
const minPower = ref(0)
|
|
||||||
const maxPower = ref(0)
|
|
||||||
const rarity = ref(0)
|
|
||||||
const origin = ref('')
|
|
||||||
const tooltip = ref('')
|
|
||||||
const isEventItem = ref(false)
|
|
||||||
|
|
||||||
const eventItems = [
|
|
||||||
{
|
|
||||||
name: 'Blessing Power',
|
|
||||||
id: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Intimacy Power',
|
|
||||||
id: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Fellow Power',
|
|
||||||
id: 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const rarities = [
|
|
||||||
{
|
|
||||||
name: 'Green',
|
|
||||||
id: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Blue',
|
|
||||||
id: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Purple',
|
|
||||||
id: 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Yellow',
|
|
||||||
id: 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Red',
|
|
||||||
id: 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const yesNo = [
|
|
||||||
{
|
|
||||||
name: 'Yes',
|
|
||||||
id: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'No',
|
|
||||||
id: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// Yes, I do want any, I don't know WHAT they have typed, but this rule is only checking that they typed SOMETHING.
|
|
||||||
const requiredField = (v: any) => (v ? true : 'This field is required.')
|
|
||||||
const textRules = [requiredField]
|
|
||||||
const minPowerRules = [
|
|
||||||
requiredField,
|
|
||||||
(v: number) =>
|
|
||||||
v <= maxPower.value ? true : 'Minimum power must be less than or equal to maximum power.'
|
|
||||||
]
|
|
||||||
const maxPowerRules = [
|
|
||||||
requiredField,
|
|
||||||
(v: number) =>
|
|
||||||
v >= minPower.value ? true : 'Maximum power must be greater than or equal to minimum power.'
|
|
||||||
]
|
|
||||||
const listToProps = (item: EventItemListing) => ({ title: item.name, value: item.id })
|
|
||||||
const yesNoProps = (item: EventItemListing) => ({ title: item.name, value: item.id === 1 })
|
|
||||||
const submit = () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
// Get the user's JWT token
|
|
||||||
getCurrentInstance()!
|
|
||||||
.appContext.config.globalProperties.$zitadel!.oidcAuth.mgr.getUser()
|
|
||||||
.then((oidcUser: User | null) => {
|
|
||||||
if (oidcUser === null) {
|
|
||||||
// TODO: Add some error handling to the page
|
|
||||||
console.log("OIDC user was null")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const newItem: PowerItem = {
|
|
||||||
itemType: itemType.value,
|
|
||||||
iconURL: iconURL.value,
|
|
||||||
itemName: itemName.value,
|
|
||||||
minItemPower: minPower.value,
|
|
||||||
maxItemPower: maxPower.value,
|
|
||||||
owned: 0,
|
|
||||||
rarity: rarity.value,
|
|
||||||
origin: origin.value,
|
|
||||||
tooltip: tooltip.value,
|
|
||||||
isEventItem: isEventItem.value
|
|
||||||
}
|
|
||||||
const userJWT = oidcUser.access_token
|
|
||||||
axios.post()
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const clearForm = () => {
|
|
||||||
itemType.value = 0
|
|
||||||
itemName.value = ''
|
|
||||||
iconURL.value = ''
|
|
||||||
minPower.value = 0
|
|
||||||
maxPower.value = 0
|
|
||||||
rarity.value = 0
|
|
||||||
origin.value = ''
|
|
||||||
tooltip.value = ''
|
|
||||||
isEventItem.value = false
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<v-container>
|
|
||||||
<v-row justify="center">
|
|
||||||
<v-col lg="8" sm="12">
|
|
||||||
<v-card :loading="loading" color="deep-purple-darken-4">
|
|
||||||
<v-card-title>Add New Item</v-card-title>
|
|
||||||
<v-card-item>
|
|
||||||
<v-form @submit.prevent>
|
|
||||||
<v-container>
|
|
||||||
<v-row>
|
|
||||||
<v-select
|
|
||||||
:items="eventItems"
|
|
||||||
:item-props="listToProps"
|
|
||||||
v-model="itemType"
|
|
||||||
label="Item Type"
|
|
||||||
></v-select>
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-text-field
|
|
||||||
v-model="itemName"
|
|
||||||
:rules="textRules"
|
|
||||||
label="Item Name"
|
|
||||||
></v-text-field>
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-text-field
|
|
||||||
v-model="iconURL"
|
|
||||||
:rules="textRules"
|
|
||||||
label="Image URL"
|
|
||||||
disabled
|
|
||||||
></v-text-field>
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-col class="pl-0">
|
|
||||||
<v-text-field
|
|
||||||
v-model="minPower"
|
|
||||||
:rules="minPowerRules"
|
|
||||||
label="Minimum Power"
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
<v-col class="pr-0">
|
|
||||||
<v-text-field
|
|
||||||
v-model="maxPower"
|
|
||||||
:rules="maxPowerRules"
|
|
||||||
label="Maximum Power"
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-select
|
|
||||||
v-model="rarity"
|
|
||||||
:items="rarities"
|
|
||||||
:item-props="listToProps"
|
|
||||||
label="Rarity"
|
|
||||||
></v-select>
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-text-field v-model="origin" :rules="textRules" label="Origin"></v-text-field>
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-text-field v-model="tooltip" :rules="textRules" label="Tooltip"></v-text-field>
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-select
|
|
||||||
v-model="isEventItem"
|
|
||||||
:items="yesNo"
|
|
||||||
:item-props="yesNoProps"
|
|
||||||
label="Is From An Event?"
|
|
||||||
></v-select>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</v-form>
|
|
||||||
</v-card-item>
|
|
||||||
<v-card-actions>
|
|
||||||
<v-btn @click="submit">Submit</v-btn>
|
|
||||||
<v-btn @click="clearForm">Clear</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</template>
|
|
||||||
|
|
@ -1,82 +1,72 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePowerItems } from '@/stores/powerItems'
|
import { usePowerItems } from '@/stores/powerItems'
|
||||||
import type { DataTableHeader } from '@/types/DataTableHeader'
|
import type { PowerItem } from '@/types/PowerItem';
|
||||||
import type { PowerItem } from '@/types/PowerItem'
|
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useDisplay } from 'vuetify'
|
|
||||||
import type { VDataTable } from 'vuetify/components'
|
|
||||||
|
|
||||||
type ReadonlyHeaders = VDataTable['headers']
|
type SortItem = { key: string, order?: boolean | 'asc' | 'desc' }
|
||||||
type SortItem = { key: string; order?: boolean | 'asc' | 'desc' }
|
|
||||||
const { lgAndUp } = useDisplay()
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
items: Map<string, PowerItem>
|
items: Map<string, PowerItem>
|
||||||
minimumTotal: number
|
minimumTotal: number
|
||||||
maximumTotal: number
|
maximumTotal: number
|
||||||
averageTotal: number
|
averageTotal: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
items: () => new Map<string, PowerItem>()
|
items: () => new Map<string, PowerItem>
|
||||||
})
|
})
|
||||||
|
|
||||||
const computedHeaders = computed((): ReadonlyHeaders => {
|
const headers = ref([
|
||||||
let initialHeaders: DataTableHeader[] = [
|
{
|
||||||
{
|
title: 'Event',
|
||||||
title: 'Name',
|
align: ' d-none d-lg-table-cell start',
|
||||||
align: 'start',
|
sortable: true,
|
||||||
sortable: true,
|
value: '1.origin'
|
||||||
value: '1.itemName'
|
},
|
||||||
},
|
{
|
||||||
{
|
title: 'Name',
|
||||||
title: 'Owned',
|
align: 'start',
|
||||||
align: 'end',
|
sortable: true,
|
||||||
sortable: true,
|
value: '1.itemName'
|
||||||
key: `1.owned`
|
},
|
||||||
},
|
{
|
||||||
{
|
title: 'Min.',
|
||||||
title: 'Mean Total',
|
align: 'end d-none d-lg-table-cell',
|
||||||
align: 'end',
|
sortable: true,
|
||||||
sortable: false,
|
key: '1.minItemPower'
|
||||||
key: `1.aveTotalPower`
|
},
|
||||||
}
|
{
|
||||||
]
|
title: 'Max.',
|
||||||
if (lgAndUp.value) {
|
align: 'end d-none d-lg-table-cell',
|
||||||
initialHeaders!.unshift({
|
sortable: true,
|
||||||
title: 'Event',
|
key: '1.maxItemPower'
|
||||||
align: 'start',
|
},
|
||||||
sortable: true,
|
{
|
||||||
value: '1.origin'
|
title: 'Owned',
|
||||||
})
|
align: 'end',
|
||||||
initialHeaders.splice(2, 0, {
|
sortable: true,
|
||||||
title: 'Min.',
|
key: `1.owned`
|
||||||
align: 'end',
|
},
|
||||||
sortable: true,
|
{
|
||||||
key: '1.minItemPower'
|
title: 'Min. Total',
|
||||||
})
|
align: 'end d-none d-lg-table-cell',
|
||||||
initialHeaders.splice(3, 0, {
|
sortable: false,
|
||||||
title: 'Max.',
|
key: `1.minTotalPower`
|
||||||
align: 'end',
|
},
|
||||||
sortable: true,
|
{
|
||||||
key: '1.maxItemPower'
|
title: 'Max. Total',
|
||||||
})
|
align: 'end d-none d-lg-table-cell',
|
||||||
initialHeaders.splice(5, 0, {
|
sortable: false,
|
||||||
title: 'Min. Total',
|
key: `1.maxTotalPower`
|
||||||
align: 'end',
|
},
|
||||||
sortable: false,
|
{
|
||||||
key: `1.minTotalPower`
|
title: 'Mean Total',
|
||||||
})
|
align: 'end',
|
||||||
initialHeaders.splice(6, 0, {
|
sortable: false,
|
||||||
title: 'Max. Total',
|
key: `1.aveTotalPower`
|
||||||
align: 'end',
|
|
||||||
sortable: false,
|
|
||||||
key: `1.maxTotalPower`
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return initialHeaders as ReadonlyHeaders
|
])
|
||||||
})
|
|
||||||
const sortBy: Ref<SortItem[]> = ref([
|
const sortBy: Ref<SortItem[]> = ref([
|
||||||
{
|
{
|
||||||
key: '1.minItemPower',
|
key: '1.minItemPower',
|
||||||
|
|
@ -111,7 +101,7 @@ const filteredItems = computed(() =>
|
||||||
? value
|
? value
|
||||||
: undefined
|
: undefined
|
||||||
)
|
)
|
||||||
.filter((value): value is [string, PowerItem] => !!value)
|
.filter((value) => value !== undefined)
|
||||||
)
|
)
|
||||||
const getColor = computed(() => (rarity: number): string => {
|
const getColor = computed(() => (rarity: number): string => {
|
||||||
if (rarity === 5) {
|
if (rarity === 5) {
|
||||||
|
|
@ -128,19 +118,11 @@ const getColor = computed(() => (rarity: number): string => {
|
||||||
})
|
})
|
||||||
const allSelected = computed(() => events.value.length === selectedEvents.value.length)
|
const allSelected = computed(() => events.value.length === selectedEvents.value.length)
|
||||||
const partialSelected = computed(() => selectedEvents.value.length > 0)
|
const partialSelected = computed(() => selectedEvents.value.length > 0)
|
||||||
const isLoading = computed((): boolean => props.items.keys()?.next().done ?? false)
|
toggle()
|
||||||
watch(isLoading, (newLoading) => {
|
|
||||||
if (!newLoading) {
|
|
||||||
toggle()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (!isLoading.value) {
|
|
||||||
toggle()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-card fluid :loading="isLoading">
|
<v-card fluid>
|
||||||
<v-card-title>Special Items</v-card-title>
|
<v-card-title>Special Items</v-card-title>
|
||||||
<v-card-subtitle>Items from events</v-card-subtitle>
|
<v-card-subtitle>Items from events</v-card-subtitle>
|
||||||
<v-card-item>
|
<v-card-item>
|
||||||
|
|
@ -169,9 +151,9 @@ if (!isLoading.value) {
|
||||||
density="compact"
|
density="compact"
|
||||||
v-model:sort-by="sortBy"
|
v-model:sort-by="sortBy"
|
||||||
:items="filteredItems"
|
:items="filteredItems"
|
||||||
:headers="computedHeaders"
|
:headers="headers"
|
||||||
>
|
>
|
||||||
<template v-slot:[`item.1.origin`]="{ item }">
|
<template v-slot:item.1.origin="{ item }">
|
||||||
<v-card
|
<v-card
|
||||||
class="my-2"
|
class="my-2"
|
||||||
elevation="2"
|
elevation="2"
|
||||||
|
|
@ -181,10 +163,10 @@ if (!isLoading.value) {
|
||||||
color="transparent"
|
color="transparent"
|
||||||
>
|
>
|
||||||
<v-img :src="getEventImageUrl(item[1].origin)"></v-img>
|
<v-img :src="getEventImageUrl(item[1].origin)"></v-img>
|
||||||
<v-tooltip activator="parent">{{ item[1].origin }}</v-tooltip> </v-card
|
<v-tooltip activator="parent">{{ item?.[1].origin }}</v-tooltip>
|
||||||
>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:[`item.1.itemName`]="{ item }">
|
<template v-slot:item.1.itemName="{ item }">
|
||||||
<v-tooltip
|
<v-tooltip
|
||||||
activator="parent"
|
activator="parent"
|
||||||
v-if="item[1].tooltip != undefined"
|
v-if="item[1].tooltip != undefined"
|
||||||
|
|
@ -201,7 +183,7 @@ if (!isLoading.value) {
|
||||||
{{ item[1].itemName }}
|
{{ item[1].itemName }}
|
||||||
</v-chip>
|
</v-chip>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:[`item.1.owned`]="{ item }">
|
<template v-slot:item.1.owned="{ item }">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
density="compact"
|
density="compact"
|
||||||
hide-details="auto"
|
hide-details="auto"
|
||||||
|
|
@ -209,13 +191,13 @@ if (!isLoading.value) {
|
||||||
@update:model-value="usePowerItems().updateOwned(item[0], item[1].owned)"
|
@update:model-value="usePowerItems().updateOwned(item[0], item[1].owned)"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:[`item.1.minTotalPower`]="{ item }">
|
<template v-slot:item.1.minTotalPower="{ item }">
|
||||||
{{ item[1].minItemPower * item[1].owned }}
|
{{ item[1].minItemPower * item[1].owned }}
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:[`item.1.maxTotalPower`]="{ item }">
|
<template v-slot:item.1.maxTotalPower="{ item }">
|
||||||
{{ item[1].maxItemPower * item[1].owned }}
|
{{ item[1].maxItemPower * item[1].owned }}
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:[`item.1.aveTotalPower`]="{ item }">
|
<template v-slot:item.1.aveTotalPower="{ item }">
|
||||||
{{ ((item[1].minItemPower + item[1].maxItemPower) / 2) * item[1].owned }}
|
{{ ((item[1].minItemPower + item[1].maxItemPower) / 2) * item[1].owned }}
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:tfoot>
|
<template v-slot:tfoot>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePowerItems } from '@/stores/powerItems'
|
import { usePowerItems } from '@/stores/powerItems'
|
||||||
import type { PowerItem } from '@/types/PowerItem'
|
import type { PowerItem } from '@/types/PowerItem'
|
||||||
import type { Ref } from 'vue';
|
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useDisplay } from 'vuetify';
|
|
||||||
import type { VDataTable } from 'vuetify/components';
|
|
||||||
|
|
||||||
type ReadonlyHeaders = VDataTable['headers']
|
|
||||||
type SortItem = { key: string; order?: boolean | 'asc' | 'desc' }
|
|
||||||
const { lgAndUp } = useDisplay()
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
items: Map<string, PowerItem>
|
items: Map<string, PowerItem>
|
||||||
|
|
@ -19,46 +12,39 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
items: () => new Map<string, PowerItem>()
|
items: () => new Map<string, PowerItem>()
|
||||||
})
|
})
|
||||||
|
|
||||||
const headers = computed((): ReadonlyHeaders => {
|
const headers = ref([
|
||||||
const initialHeaders = [
|
{
|
||||||
{
|
title: '',
|
||||||
title: '',
|
align: 'start',
|
||||||
align: 'start',
|
sortable: false,
|
||||||
sortable: false,
|
value: '1.iconURL'
|
||||||
value: '1.iconURL'
|
},
|
||||||
},
|
{
|
||||||
{
|
title: 'Name',
|
||||||
title: 'Owned',
|
align: ' d-none d-lg-table-cell start',
|
||||||
align: 'end',
|
sortable: true,
|
||||||
sortable: true,
|
value: '1.itemName'
|
||||||
key: `1.owned`
|
},
|
||||||
},
|
{
|
||||||
{
|
title: 'Power',
|
||||||
title: 'Total',
|
align: ' d-none d-lg-table-cell',
|
||||||
align: 'end',
|
sortable: true,
|
||||||
sortable: false,
|
key: '1.minItemPower'
|
||||||
key: `1.totalPower`
|
},
|
||||||
}
|
{
|
||||||
]
|
title: 'Owned',
|
||||||
if (lgAndUp.value) {
|
align: 'end',
|
||||||
initialHeaders!.splice(1, 0,
|
sortable: true,
|
||||||
{
|
key: `1.owned`
|
||||||
title: 'Name',
|
},
|
||||||
align: 'start',
|
{
|
||||||
sortable: true,
|
title: 'Total',
|
||||||
value: '1.itemName'
|
align: 'end',
|
||||||
})
|
sortable: false,
|
||||||
initialHeaders!.splice(2, 0,
|
key: `1.totalPower`
|
||||||
{
|
|
||||||
title: 'Power',
|
|
||||||
align: 'end',
|
|
||||||
sortable: true,
|
|
||||||
key: '1.minItemPower'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return initialHeaders as ReadonlyHeaders
|
])
|
||||||
})
|
const sortBy = ref([
|
||||||
const sortBy: Ref<SortItem[]> = ref([
|
|
||||||
{
|
{
|
||||||
key: '1.minItemPower',
|
key: '1.minItemPower',
|
||||||
order: 'asc'
|
order: 'asc'
|
||||||
|
|
@ -78,11 +64,10 @@ const getColor = computed(() => (rarity: number): string => {
|
||||||
return 'green'
|
return 'green'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const isLoading = computed((): boolean => props.items.keys()?.next().done ?? false)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-card :loading="isLoading">
|
<v-card>
|
||||||
<v-card-title>Standard Items</v-card-title>
|
<v-card-title>Standard Items</v-card-title>
|
||||||
<v-card-subtitle>Items that exist all the time</v-card-subtitle>
|
<v-card-subtitle>Items that exist all the time</v-card-subtitle>
|
||||||
<v-card-item>
|
<v-card-item>
|
||||||
|
|
@ -92,7 +77,7 @@ const isLoading = computed((): boolean => props.items.keys()?.next().done ?? fal
|
||||||
:items="[...items.entries()]"
|
:items="[...items.entries()]"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
>
|
>
|
||||||
<template v-slot:[`item.1.iconURL`]="{ item }">
|
<template v-slot:item.1.iconURL="{ item }">
|
||||||
<v-card class="my-2" elevation="2" rounded width="41">
|
<v-card class="my-2" elevation="2" rounded width="41">
|
||||||
<v-tooltip
|
<v-tooltip
|
||||||
activator="parent"
|
activator="parent"
|
||||||
|
|
@ -109,7 +94,7 @@ const isLoading = computed((): boolean => props.items.keys()?.next().done ?? fal
|
||||||
<v-img :src="`/images/${item[1].iconURL}`" height="41" width="41" cover></v-img>
|
<v-img :src="`/images/${item[1].iconURL}`" height="41" width="41" cover></v-img>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:[`item.1.itemName`]="{ item }">
|
<template v-slot:item.1.itemName="{ item }">
|
||||||
<v-tooltip
|
<v-tooltip
|
||||||
activator="parent"
|
activator="parent"
|
||||||
v-if="item[1].tooltip != undefined"
|
v-if="item[1].tooltip != undefined"
|
||||||
|
|
@ -126,7 +111,7 @@ const isLoading = computed((): boolean => props.items.keys()?.next().done ?? fal
|
||||||
{{ item[1].itemName }}
|
{{ item[1].itemName }}
|
||||||
</v-chip>
|
</v-chip>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:[`item.1.owned`]="{ item }">
|
<template v-slot:item.1.owned="{ item }">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
density="compact"
|
density="compact"
|
||||||
hide-details="auto"
|
hide-details="auto"
|
||||||
|
|
@ -134,7 +119,7 @@ const isLoading = computed((): boolean => props.items.keys()?.next().done ?? fal
|
||||||
@update:model-value="usePowerItems().updateOwned(item[0], item[1].owned)"
|
@update:model-value="usePowerItems().updateOwned(item[0], item[1].owned)"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:[`item.1.totalPower`]="{ item }">
|
<template v-slot:item.1.totalPower="{ item }">
|
||||||
{{ item[1].minItemPower * item[1].owned }}
|
{{ item[1].minItemPower * item[1].owned }}
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:tfoot>
|
<template v-slot:tfoot>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
itemType: string
|
standardTotal: number
|
||||||
standardTotal: number
|
minimumTotal: number
|
||||||
minimumTotal: number
|
maximumTotal: number
|
||||||
maximumTotal: number
|
averageTotal: number
|
||||||
averageTotal: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
@ -18,17 +17,17 @@ const aveIncrease = computed(() => props.standardTotal + props.averageTotal)
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-title>Overall {{ itemType }} Power Increase</v-card-title>
|
<v-card-title>Overall Blessing Power Increase</v-card-title>
|
||||||
<v-card-item>
|
<v-card-item>
|
||||||
<v-list density="compact">
|
<v-list density="compact">
|
||||||
<v-list-item>
|
<v-list-item>
|
||||||
<v-list-item-title>Minimum: {{ minIncrease }}</v-list-item-title>
|
<v-list-item-title>Minimum: {{ minIncrease }}</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item>
|
<v-list-item>
|
||||||
<v-list-item-title>Maximum: {{ maxIncrease }}</v-list-item-title>
|
<v-list-item-title>Maximum: {{ maxIncrease }}</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item>
|
<v-list-item>
|
||||||
<v-list-item-title>Mean: {{ aveIncrease }}</v-list-item-title>
|
<v-list-item-title>Mean: {{ aveIncrease }}</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-card-item>
|
</v-card-item>
|
||||||
|
|
|
||||||
48
src/main.ts
48
src/main.ts
|
|
@ -2,52 +2,32 @@ import './assets/main.css'
|
||||||
|
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
|
||||||
// Vuetify
|
// Vuetify
|
||||||
import 'vuetify/styles'
|
import 'vuetify/styles'
|
||||||
import '@mdi/font/css/materialdesignicons.css'
|
import '@mdi/font/css/materialdesignicons.css'
|
||||||
import { createVuetify } from 'vuetify'
|
import { createVuetify } from 'vuetify'
|
||||||
|
import * as components from 'vuetify/components'
|
||||||
import App from './App.vue'
|
import * as directives from 'vuetify/directives'
|
||||||
import { apiBaseURL, oidc } from './types/ConfigSymbols'
|
|
||||||
import { configureOidc } from './services/authentikAuth'
|
|
||||||
import router from './router'
|
|
||||||
import { getConfig } from './services/siteConfig'
|
|
||||||
import type { Config } from './types/Config'
|
|
||||||
|
|
||||||
const vuetify = createVuetify({
|
const vuetify = createVuetify({
|
||||||
|
components,
|
||||||
|
directives,
|
||||||
icons: {
|
icons: {
|
||||||
defaultSet: 'mdi'
|
defaultSet: 'mdi',
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
defaultTheme: 'dark'
|
defaultTheme: 'dark'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
getConfig().then((conf: Config | null) => {
|
const app = createApp(App)
|
||||||
configureOidc().then((oidcAuth) => {
|
|
||||||
if (conf === null) {
|
|
||||||
throw new Error('config was null')
|
|
||||||
}
|
|
||||||
const app = createApp(App)
|
|
||||||
|
|
||||||
app.provide(apiBaseURL, conf.apiBaseURL)
|
app.use(createPinia())
|
||||||
|
app.use(router)
|
||||||
|
app.use(vuetify)
|
||||||
|
|
||||||
oidcAuth.startup().then((ok: boolean) => {
|
app.mount('#app')
|
||||||
if (ok) {
|
|
||||||
app.provide(oidc, oidcAuth)
|
|
||||||
|
|
||||||
const pinia = createPinia()
|
|
||||||
|
|
||||||
app.use(router)
|
|
||||||
app.use(vuetify)
|
|
||||||
app.use(pinia)
|
|
||||||
|
|
||||||
app.mount('#app')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch((reason) => {
|
|
||||||
console.log(reason)
|
|
||||||
throw new Error(reason)
|
|
||||||
})
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,5 @@
|
||||||
import { configureOidc } from '@/services/authentikAuth'
|
|
||||||
import { getConfig } from '@/services/siteConfig'
|
|
||||||
import type { Config } from '@/types/Config'
|
|
||||||
import { LOGIN_RETURN_URL_STORAGE } from '@/types/ConfigSymbols'
|
|
||||||
import type { OidcAuth } from 'vue-oidc-client/vue3'
|
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
let config: Config = {} as Config
|
|
||||||
getConfig().then((conf: Config | null): Config => (config = conf !== null ? conf : ({} as Config)))
|
|
||||||
let oidcAuth: OidcAuth = {} as OidcAuth
|
|
||||||
configureOidc().then((auth: OidcAuth) => {
|
|
||||||
oidcAuth = auth
|
|
||||||
oidcAuth!.useRouter(router)
|
|
||||||
})
|
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
|
|
@ -22,70 +9,26 @@ const router = createRouter({
|
||||||
component: () => import('@/views/HomeView.vue')
|
component: () => import('@/views/HomeView.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/blessing-power',
|
path: "/blessing-power",
|
||||||
name: 'blessing-power',
|
name: "blessing-power",
|
||||||
component: () => import('@/views/BlessingPowerView.vue')
|
component: () => import('@/views/BlessingPowerView.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/fellow-power',
|
path: "/fellow-power",
|
||||||
name: 'fellow-power',
|
name: "fellow-power",
|
||||||
component: () => import('@/views/FellowPowerView.vue')
|
component: () => import('@/views/FellowPowerView.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/intimacy-power',
|
path: "/intimacy-power",
|
||||||
name: 'intimacy-power',
|
name: "intimacy-power",
|
||||||
component: () => import('@/views/IntimacyPowerView.vue')
|
component: () => import('@/views/IntimacyPowerView.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/test',
|
path: "/test",
|
||||||
name: 'test',
|
name: "test",
|
||||||
component: () =>
|
component: () => import('@/views/Test.vue')
|
||||||
hasRole('admin') ? import('@/views/TestView.vue') : import('@/views/NoAccess.vue')
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
path: '/login',
|
|
||||||
name: 'login',
|
|
||||||
meta: {
|
|
||||||
authName: oidcAuth.authName
|
|
||||||
},
|
|
||||||
component: () => import('@/views/LoginView.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/admin',
|
|
||||||
name: 'admin',
|
|
||||||
meta: {
|
|
||||||
authName: oidcAuth.authName
|
|
||||||
},
|
|
||||||
component: () =>
|
|
||||||
hasRole('admin') ? import('@/views/Admin.vue') : import('@/views/NoAccess.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/postLogin',
|
|
||||||
name: 'postLogin',
|
|
||||||
redirect: () => {
|
|
||||||
let redirectUrl = sessionStorage.getItem(LOGIN_RETURN_URL_STORAGE)
|
|
||||||
if (redirectUrl === "" || redirectUrl === null) {
|
|
||||||
redirectUrl = "/"
|
|
||||||
}
|
|
||||||
sessionStorage.removeItem(LOGIN_RETURN_URL_STORAGE)
|
|
||||||
return { path: redirectUrl }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
const hasRole = (role: string) => {
|
|
||||||
if (config.oidcProjectID === undefined) {
|
|
||||||
throw new Error('Config was not loaded')
|
|
||||||
}
|
|
||||||
const roles = oidcAuth.userProfile[
|
|
||||||
`urn:zitadel:iam:org:project:${config.oidcProjectID}:roles`
|
|
||||||
] as Array<any>
|
|
||||||
if (!roles) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return roles.find((r) => r[role])
|
|
||||||
}
|
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
import type { User } from 'oidc-client'
|
|
||||||
import { createOidcAuth, SignInType, type OidcAuth, LogLevel } from 'vue-oidc-client/vue3'
|
|
||||||
import { getConfig } from './siteConfig'
|
|
||||||
import type { Config } from '@/types/Config'
|
|
||||||
|
|
||||||
const loco = window.location
|
|
||||||
const appRootUrl = `${loco.protocol}//${loco.host}${import.meta.env.BASE_URL}`
|
|
||||||
|
|
||||||
let authObj = null as OidcAuth | null
|
|
||||||
|
|
||||||
export async function configureOidc() {
|
|
||||||
if (authObj) return Promise.resolve(authObj)
|
|
||||||
const appConfig: Config = await getConfig().then((c) => {
|
|
||||||
if (c === null) {
|
|
||||||
throw new Error("config was null")
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}).catch((reason) => {
|
|
||||||
console.log(reason)
|
|
||||||
throw new Error(reason);
|
|
||||||
})
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
authority: appConfig.oidcAuthority,
|
|
||||||
client_id: appConfig.oidcClientID,
|
|
||||||
response_type: 'code',
|
|
||||||
scope: 'openid profile email api offline_access'
|
|
||||||
}
|
|
||||||
|
|
||||||
authObj = createOidcAuth(
|
|
||||||
'main',
|
|
||||||
SignInType.Window,
|
|
||||||
appRootUrl,
|
|
||||||
{
|
|
||||||
...config,
|
|
||||||
// test use
|
|
||||||
prompt: 'login'
|
|
||||||
},
|
|
||||||
console,
|
|
||||||
LogLevel.Debug
|
|
||||||
)
|
|
||||||
|
|
||||||
// handle events
|
|
||||||
authObj.events.addAccessTokenExpiring(function () {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('access token expiring')
|
|
||||||
})
|
|
||||||
|
|
||||||
authObj.events.addAccessTokenExpired(function () {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('access token expired')
|
|
||||||
})
|
|
||||||
|
|
||||||
authObj.events.addSilentRenewError(function (err: Error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error('silent renew error', err)
|
|
||||||
})
|
|
||||||
|
|
||||||
authObj.events.addUserLoaded(function (user: User) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('user loaded', user)
|
|
||||||
})
|
|
||||||
|
|
||||||
authObj.events.addUserUnloaded(function () {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('user unloaded')
|
|
||||||
})
|
|
||||||
|
|
||||||
authObj.events.addUserSignedOut(function () {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('user signed out')
|
|
||||||
})
|
|
||||||
|
|
||||||
authObj.events.addUserSessionChanged(function () {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('user session changed')
|
|
||||||
})
|
|
||||||
|
|
||||||
return authObj
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import type { Config } from '@/types/Config'
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
let configObject = null as Config | null
|
|
||||||
|
|
||||||
export async function getConfig() {
|
|
||||||
if (configObject) return Promise.resolve(configObject)
|
|
||||||
|
|
||||||
configObject = await axios.get('/config.json?noCache=' + Date.now()).then((resp) => {
|
|
||||||
return {
|
|
||||||
apiBaseURL: resp.data.apiBaseURL,
|
|
||||||
oidcAuthority: resp.data.oidcAuthority,
|
|
||||||
oidcClientID: resp.data.oidcClientID,
|
|
||||||
oidcProjectID: resp.data.oidcProjectID
|
|
||||||
} as Config
|
|
||||||
})
|
|
||||||
return configObject
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
import {createZITADELAuth} from "@zitadel/vue";
|
|
||||||
import {User} from "oidc-client";
|
|
||||||
|
|
||||||
const zitadelAuth = createZITADELAuth({
|
|
||||||
project_resource_id: '252968011466539011',
|
|
||||||
client_id: "252968365600079875@isl",
|
|
||||||
issuer: "http://localhost:8080/",
|
|
||||||
})
|
|
||||||
|
|
||||||
// handle events
|
|
||||||
zitadelAuth.oidcAuth.events.addAccessTokenExpiring(function() {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('access token expiring')
|
|
||||||
})
|
|
||||||
|
|
||||||
zitadelAuth.oidcAuth.events.addAccessTokenExpired(function() {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('access token expired')
|
|
||||||
})
|
|
||||||
|
|
||||||
zitadelAuth.oidcAuth.events.addSilentRenewError(function(err: Error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error('silent renew error', err)
|
|
||||||
})
|
|
||||||
|
|
||||||
zitadelAuth.oidcAuth.events.addUserLoaded(function(user: User) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('user loaded', user)
|
|
||||||
})
|
|
||||||
|
|
||||||
zitadelAuth.oidcAuth.events.addUserUnloaded(function() {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('user unloaded')
|
|
||||||
})
|
|
||||||
|
|
||||||
zitadelAuth.oidcAuth.events.addUserSignedOut(function() {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('user signed out')
|
|
||||||
})
|
|
||||||
|
|
||||||
zitadelAuth.oidcAuth.events.addUserSessionChanged(function() {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('user session changed')
|
|
||||||
})
|
|
||||||
|
|
||||||
export default zitadelAuth
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
export const useGeneralState = defineStore('general', () => {
|
|
||||||
const LOGIN_RETURN_URL_STORAGE = 'LOGIN_RETURN_URL_STORAGE'
|
|
||||||
|
|
||||||
const loginReturnUrl = ref("")
|
|
||||||
|
|
||||||
function setLoginReturnUrl(url: string) {
|
|
||||||
loginReturnUrl.value = url
|
|
||||||
localStorage.setItem(
|
|
||||||
LOGIN_RETURN_URL_STORAGE,
|
|
||||||
loginReturnUrl.value,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLoginReturnUrl(): string {
|
|
||||||
const storedUrl = localStorage.getItem(LOGIN_RETURN_URL_STORAGE)
|
|
||||||
if (storedUrl === null || storedUrl === undefined || storedUrl === "") {
|
|
||||||
localStorage.removeItem(LOGIN_RETURN_URL_STORAGE)
|
|
||||||
const res = loginReturnUrl.value
|
|
||||||
loginReturnUrl.value == null
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
localStorage.removeItem(LOGIN_RETURN_URL_STORAGE)
|
|
||||||
loginReturnUrl.value == null
|
|
||||||
return storedUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
setLoginReturnUrl,
|
|
||||||
getLoginReturnUrl
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
@ -1,56 +1,26 @@
|
||||||
import type { PowerItem } from '@/types/PowerItem'
|
import type { PowerItem } from '@/types/PowerItem'
|
||||||
import axios, { type AxiosRequestConfig } from 'axios'
|
import axios from 'axios'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { computed, ref, toRaw } from 'vue'
|
import { computed, ref, toRaw } from 'vue'
|
||||||
import axiosRetry from 'axios-retry'
|
|
||||||
import { apiBaseURL as apiBaseURLKey } from '@/types/ConfigSymbols'
|
|
||||||
import { inject } from 'vue'
|
|
||||||
import { getConfig } from '@/services/siteConfig'
|
|
||||||
import type { Config } from '@/types/Config'
|
|
||||||
|
|
||||||
const BLESSING = 1
|
|
||||||
const INTIMACY = 2
|
|
||||||
const FELLOW = 3
|
|
||||||
|
|
||||||
const noCacheConfig: AxiosRequestConfig = {
|
|
||||||
responseType: 'json',
|
|
||||||
headers: {
|
|
||||||
'Cache-Control': 'no-cache',
|
|
||||||
Pragma: 'no-cache',
|
|
||||||
Expires: '0'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const usePowerItems = defineStore('powerItems', () => {
|
export const usePowerItems = defineStore('powerItems', () => {
|
||||||
//#region privateVariables
|
|
||||||
const BLESSING_POWER_ITEM_STORAGE = 'BLESSING_POWER_ITEM_STORAGE'
|
const BLESSING_POWER_ITEM_STORAGE = 'BLESSING_POWER_ITEM_STORAGE'
|
||||||
const FELLOW_POWER_ITEM_STORAGE = 'FELLOW_POWER_ITEM_STORAGE'
|
const FELLOW_POWER_ITEM_STORAGE = 'FELLOW_POWER_ITEM_STORAGE'
|
||||||
const INTIMACY_POWER_ITEM_STORAGE = 'INTIMACY_POWER_ITEM_STORAGE'
|
const INTIMACY_POWER_ITEM_STORAGE = 'INTIMACY_POWER_ITEM_STORAGE'
|
||||||
let apiBaseURL = inject(apiBaseURLKey)!
|
|
||||||
if (apiBaseURL === undefined) {
|
|
||||||
getConfig().then((conf: Config | null) => {
|
|
||||||
if (conf !== null) {
|
|
||||||
apiBaseURL = conf.apiBaseURL
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region state
|
|
||||||
const blessingPowerItems = ref(new Map<string, PowerItem>())
|
const blessingPowerItems = ref(new Map<string, PowerItem>())
|
||||||
const fellowPowerItems = ref(new Map<string, PowerItem>())
|
const fellowPowerItems = ref(new Map<string, PowerItem>())
|
||||||
const intimacyPowerItems = ref(new Map<string, PowerItem>())
|
const intimacyPowerItems = ref(new Map<string, PowerItem>())
|
||||||
//#endregion
|
const isLoadComplete = ref(false)
|
||||||
|
|
||||||
axiosRetry(axios, {
|
async function fetchPowerItems() {
|
||||||
retries: 3,
|
|
||||||
retryDelay: axiosRetry.exponentialDelay
|
|
||||||
})
|
|
||||||
|
|
||||||
//#region loaders
|
|
||||||
async function fetchBlessingItems() {
|
|
||||||
axios
|
axios
|
||||||
.get(apiBaseURL + '/powerItems/byType/' + BLESSING + '/asMap', noCacheConfig)
|
.get('/items/blessingPowerItems.json', {
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
Pragma: 'no-cache',
|
||||||
|
Expires: '0'
|
||||||
|
}
|
||||||
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
const plainMap = new Map<string, PowerItem>(
|
const plainMap = new Map<string, PowerItem>(
|
||||||
Object.entries(JSON.parse(localStorage.getItem(BLESSING_POWER_ITEM_STORAGE) || '{}'))
|
Object.entries(JSON.parse(localStorage.getItem(BLESSING_POWER_ITEM_STORAGE) || '{}'))
|
||||||
|
|
@ -65,10 +35,14 @@ export const usePowerItems = defineStore('powerItems', () => {
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
async function fetchFellowItems() {
|
|
||||||
axios
|
axios
|
||||||
.get(apiBaseURL + '/powerItems/byType/' + FELLOW + '/asMap', noCacheConfig)
|
.get('/items/fellowPowerItems.json', {
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
Pragma: 'no-cache',
|
||||||
|
Expires: '0'
|
||||||
|
}
|
||||||
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
const plainMap = new Map<string, PowerItem>(
|
const plainMap = new Map<string, PowerItem>(
|
||||||
Object.entries(JSON.parse(localStorage.getItem(FELLOW_POWER_ITEM_STORAGE) || '{}'))
|
Object.entries(JSON.parse(localStorage.getItem(FELLOW_POWER_ITEM_STORAGE) || '{}'))
|
||||||
|
|
@ -83,10 +57,14 @@ export const usePowerItems = defineStore('powerItems', () => {
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
async function fetchIntimacyItems() {
|
|
||||||
axios
|
axios
|
||||||
.get(apiBaseURL + '/powerItems/byType/' + INTIMACY + '/asMap', noCacheConfig)
|
.get('/items/intimacyPowerItems.json', {
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
Pragma: 'no-cache',
|
||||||
|
Expires: '0'
|
||||||
|
}
|
||||||
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
const plainMap = new Map<string, PowerItem>(
|
const plainMap = new Map<string, PowerItem>(
|
||||||
Object.entries(JSON.parse(localStorage.getItem(INTIMACY_POWER_ITEM_STORAGE) || '{}'))
|
Object.entries(JSON.parse(localStorage.getItem(INTIMACY_POWER_ITEM_STORAGE) || '{}'))
|
||||||
|
|
@ -101,10 +79,9 @@ export const usePowerItems = defineStore('powerItems', () => {
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
})
|
})
|
||||||
|
isLoadComplete.value = true
|
||||||
}
|
}
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region setters
|
|
||||||
function updateOwned(key: string, newOwned: number) {
|
function updateOwned(key: string, newOwned: number) {
|
||||||
let cur = blessingPowerItems.value.get(key)
|
let cur = blessingPowerItems.value.get(key)
|
||||||
if (cur !== undefined) {
|
if (cur !== undefined) {
|
||||||
|
|
@ -136,21 +113,16 @@ export const usePowerItems = defineStore('powerItems', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPowerItem(newItem: PowerItem): Promise<PowerItem | null> {
|
function mapToObj(map: Map<string, PowerItem>) {
|
||||||
const resultPromise: Promise<PowerItem | null> = new Promise((resolve, reject) => {
|
return Array.from(map).reduce((obj, [key, value]) => {
|
||||||
axios
|
// Doing weird magic to work with maps is infuriating, and I haven't found a better solution for this.
|
||||||
.post(apiBaseURL + '/powerItems/', newItem, noCacheConfig)
|
// So ignore that error TypeScript, I (don't) know what I'm doing!
|
||||||
.then((resp) => {
|
// @ts-ignore: noImplicitAny
|
||||||
resolve(resp.data)
|
obj[key] = value
|
||||||
})
|
return obj
|
||||||
.catch((rejected) => reject(rejected))
|
}, {})
|
||||||
})
|
|
||||||
|
|
||||||
return resultPromise
|
|
||||||
}
|
}
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region computed
|
|
||||||
const totalBlessingPower = computed(() =>
|
const totalBlessingPower = computed(() =>
|
||||||
[...blessingPowerItems.value.values()].reduce(
|
[...blessingPowerItems.value.values()].reduce(
|
||||||
(accumulator: number, currentValue: PowerItem) => {
|
(accumulator: number, currentValue: PowerItem) => {
|
||||||
|
|
@ -333,29 +305,15 @@ export const usePowerItems = defineStore('powerItems', () => {
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region helpers
|
fetchPowerItems()
|
||||||
function mapToObj(map: Map<string, PowerItem>) {
|
|
||||||
return Array.from(map).reduce((obj, [key, value]) => {
|
|
||||||
// Doing weird magic to work with maps is infuriating, and I haven't found a better solution for this.
|
|
||||||
// So ignore that error TypeScript, I (don't) know what I'm doing!
|
|
||||||
// @ts-ignore: noImplicitAny
|
|
||||||
obj[key] = value
|
|
||||||
return obj
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
blessingPowerItems,
|
blessingPowerItems,
|
||||||
fellowPowerItems,
|
fellowPowerItems,
|
||||||
intimacyPowerItems,
|
intimacyPowerItems,
|
||||||
|
isLoadComplete,
|
||||||
fetchBlessingItems,
|
fetchPowerItems,
|
||||||
fetchFellowItems,
|
|
||||||
fetchIntimacyItems,
|
|
||||||
|
|
||||||
updateOwned,
|
updateOwned,
|
||||||
totalBlessingPower,
|
totalBlessingPower,
|
||||||
standardBlessingItems,
|
standardBlessingItems,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
export type Config = {
|
|
||||||
apiBaseURL: string,
|
|
||||||
oidcAuthority: string,
|
|
||||||
oidcClientID: string,
|
|
||||||
oidcProjectID: string,
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import type { InjectionKey } from "vue";
|
|
||||||
import type { OidcAuth } from "vue-oidc-client/vue3";
|
|
||||||
|
|
||||||
const apiBaseURL = Symbol() as InjectionKey<string>
|
|
||||||
const oidcProjectID = Symbol() as InjectionKey<string>
|
|
||||||
const oidc = Symbol() as InjectionKey<OidcAuth>
|
|
||||||
|
|
||||||
const LOGIN_RETURN_URL_STORAGE = "LOGIN_RETURN_URL_STORAGE"
|
|
||||||
|
|
||||||
export {
|
|
||||||
apiBaseURL,
|
|
||||||
oidcProjectID,
|
|
||||||
oidc,
|
|
||||||
LOGIN_RETURN_URL_STORAGE
|
|
||||||
}
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
import type { ComputedRef } from "vue"
|
|
||||||
import type { Ref } from "vue"
|
|
||||||
|
|
||||||
export type DataTableHeader = {
|
|
||||||
key?: 'data-table-group' | 'data-table-select' | 'data-table-expand' | (string & {})
|
|
||||||
value?: SelectItemKey
|
|
||||||
title?: string
|
|
||||||
fixed?: boolean
|
|
||||||
align?: 'start' | 'end' | 'center'
|
|
||||||
width?: number | string
|
|
||||||
minWidth?: string
|
|
||||||
maxWidth?: string
|
|
||||||
headerProps?: Record<string, any>
|
|
||||||
cellProps?: HeaderCellProps
|
|
||||||
sortable?: boolean
|
|
||||||
sort?: DataTableCompareFunction
|
|
||||||
filter?: FilterFunction
|
|
||||||
children?: DataTableHeader[]
|
|
||||||
}
|
|
||||||
|
|
||||||
type SelectItemKey<T = Record<string, any>> = boolean | null | undefined | string | readonly (string | number)[] | ((item: T, fallback?: any) => any);
|
|
||||||
type HeaderCellProps = Record<string, any> | ((data: Pick<ItemKeySlot<any>, 'index' | 'item' | 'internalItem' | 'value'>) => Record<string, any>);
|
|
||||||
type DataTableCompareFunction<T = any> = (a: T, b: T) => number;
|
|
||||||
type FilterFunction = (value: string, query: string, item?: InternalItem) => FilterMatch;
|
|
||||||
type ItemKeySlot<T> = ItemSlotBase<T> & {
|
|
||||||
value: any;
|
|
||||||
column: InternalDataTableHeader;
|
|
||||||
};
|
|
||||||
type FilterMatch = boolean | number | [number, number] | [number, number][];
|
|
||||||
type ItemSlotBase<T> = {
|
|
||||||
index: number;
|
|
||||||
item: T;
|
|
||||||
internalItem: DataTableItem<T>;
|
|
||||||
isExpanded: ReturnType<typeof provideExpanded>['isExpanded'];
|
|
||||||
toggleExpand: ReturnType<typeof provideExpanded>['toggleExpand'];
|
|
||||||
isSelected: ReturnType<typeof provideSelection>['isSelected'];
|
|
||||||
toggleSelect: ReturnType<typeof provideSelection>['toggleSelect'];
|
|
||||||
};
|
|
||||||
type InternalDataTableHeader = Omit<DataTableHeader, 'key' | 'value' | 'children'> & {
|
|
||||||
key: string | null;
|
|
||||||
value: SelectItemKey | null;
|
|
||||||
sortable: boolean;
|
|
||||||
fixedOffset?: number;
|
|
||||||
lastFixed?: boolean;
|
|
||||||
colspan?: number;
|
|
||||||
rowspan?: number;
|
|
||||||
children?: InternalDataTableHeader[];
|
|
||||||
};
|
|
||||||
type ExpandProps = {
|
|
||||||
expandOnClick: boolean;
|
|
||||||
expanded: readonly string[];
|
|
||||||
'onUpdate:expanded': ((value: any[]) => void) | undefined;
|
|
||||||
};
|
|
||||||
type SelectionProps = Pick<DataTableItemProps, 'itemValue'> & {
|
|
||||||
modelValue: readonly any[];
|
|
||||||
selectStrategy: 'single' | 'page' | 'all';
|
|
||||||
valueComparator: typeof deepEqual;
|
|
||||||
'onUpdate:modelValue': EventProp<[any[]]> | undefined;
|
|
||||||
};
|
|
||||||
type EventProp<T extends any[] = any[], F = (...args: T) => void> = F;
|
|
||||||
|
|
||||||
declare function provideExpanded(props: ExpandProps): {
|
|
||||||
expand: (item: DataTableItem, value: boolean) => void;
|
|
||||||
expanded: Ref<Set<string>> & {
|
|
||||||
readonly externalValue: readonly string[];
|
|
||||||
};
|
|
||||||
expandOnClick: Ref<boolean>;
|
|
||||||
isExpanded: (item: DataTableItem) => boolean;
|
|
||||||
toggleExpand: (item: DataTableItem) => void;
|
|
||||||
};
|
|
||||||
declare function provideSelection(props: SelectionProps, { allItems, currentPage }: {
|
|
||||||
allItems: Ref<SelectableItem[]>;
|
|
||||||
currentPage: Ref<SelectableItem[]>;
|
|
||||||
}): {
|
|
||||||
toggleSelect: (item: SelectableItem) => void;
|
|
||||||
select: (items: SelectableItem[], value: boolean) => void;
|
|
||||||
selectAll: (value: boolean) => void;
|
|
||||||
isSelected: (items: SelectableItem | SelectableItem[]) => boolean;
|
|
||||||
isSomeSelected: (items: SelectableItem | SelectableItem[]) => boolean;
|
|
||||||
someSelected: ComputedRef<boolean>;
|
|
||||||
allSelected: ComputedRef<boolean>;
|
|
||||||
showSelectAll: boolean;
|
|
||||||
};
|
|
||||||
declare function deepEqual(a: any, b: any): boolean;
|
|
||||||
|
|
||||||
interface InternalItem<T = any> {
|
|
||||||
value: any;
|
|
||||||
raw: T;
|
|
||||||
}
|
|
||||||
interface DataTableItemProps {
|
|
||||||
items: any[];
|
|
||||||
itemValue: SelectItemKey;
|
|
||||||
itemSelectable: SelectItemKey;
|
|
||||||
returnObject: boolean;
|
|
||||||
}
|
|
||||||
interface DataTableItem<T = any> extends InternalItem<T>, GroupableItem<T>, SelectableItem {
|
|
||||||
key: any;
|
|
||||||
index: number;
|
|
||||||
columns: {
|
|
||||||
[key: string]: any;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
interface SelectableItem {
|
|
||||||
value: any;
|
|
||||||
selectable: boolean;
|
|
||||||
}
|
|
||||||
interface GroupableItem<T = any> {
|
|
||||||
type: 'item';
|
|
||||||
raw: T;
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
export type PowerItem = {
|
export type PowerItem = {
|
||||||
itemType: number
|
|
||||||
iconURL: string
|
iconURL: string
|
||||||
itemName: string
|
itemName: string
|
||||||
minItemPower: number
|
minItemPower: number
|
||||||
|
|
@ -8,5 +7,5 @@ export type PowerItem = {
|
||||||
rarity: number
|
rarity: number
|
||||||
origin: string
|
origin: string
|
||||||
tooltip: string
|
tooltip: string
|
||||||
isEventItem: boolean
|
isEventItem: string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="admin">
|
|
||||||
<div>
|
|
||||||
<h1>
|
|
||||||
This is an administrator page.
|
|
||||||
</h1>
|
|
||||||
<h2>
|
|
||||||
You can see it because you have the role "admin" in your ZITADEL project.
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
@media (min-width: 1024px) {
|
|
||||||
.admin {
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -4,7 +4,6 @@ import StandardItemsCard from '@/components/StandardItemsCard.vue'
|
||||||
import SummaryCard from '@/components/SummaryCard.vue'
|
import SummaryCard from '@/components/SummaryCard.vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { usePowerItems } from '@/stores/powerItems'
|
import { usePowerItems } from '@/stores/powerItems'
|
||||||
import { onBeforeMount } from 'vue'
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
standardBlessingItems,
|
standardBlessingItems,
|
||||||
|
|
@ -14,19 +13,10 @@ const {
|
||||||
specialBlessingItemsMaxTotal,
|
specialBlessingItemsMaxTotal,
|
||||||
specialBlessingItemsAveTotal
|
specialBlessingItemsAveTotal
|
||||||
} = storeToRefs(usePowerItems())
|
} = storeToRefs(usePowerItems())
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
if (standardBlessingItems.value.keys().next().done) {
|
|
||||||
usePowerItems().fetchBlessingItems()
|
|
||||||
}
|
|
||||||
if (specialBlessingItems.value.keys().next().done) {
|
|
||||||
usePowerItems().fetchBlessingItems()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-card class="flex-fill">
|
<v-card>
|
||||||
<v-card-title>Blessing Power</v-card-title>
|
<v-card-title>Blessing Power</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-sheet class="d-flex flex-wrap flex-fill">
|
<v-sheet class="d-flex flex-wrap flex-fill">
|
||||||
|
|
@ -44,7 +34,6 @@ onBeforeMount(() => {
|
||||||
/>
|
/>
|
||||||
<SummaryCard
|
<SummaryCard
|
||||||
class="ma-2 align-self-start"
|
class="ma-2 align-self-start"
|
||||||
item-type="Blessing"
|
|
||||||
:standard-total="standardBlessingItemTotal"
|
:standard-total="standardBlessingItemTotal"
|
||||||
:minimum-total="specialBlessingItemsMinTotal"
|
:minimum-total="specialBlessingItemsMinTotal"
|
||||||
:maximum-total="specialBlessingItemsMaxTotal"
|
:maximum-total="specialBlessingItemsMaxTotal"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import StandardItemsCard from '@/components/StandardItemsCard.vue'
|
||||||
import SummaryCard from '@/components/SummaryCard.vue'
|
import SummaryCard from '@/components/SummaryCard.vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { usePowerItems } from '@/stores/powerItems'
|
import { usePowerItems } from '@/stores/powerItems'
|
||||||
import { onBeforeMount } from 'vue'
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
standardFellowItems,
|
standardFellowItems,
|
||||||
|
|
@ -14,19 +13,10 @@ const {
|
||||||
specialFellowItemsMaxTotal,
|
specialFellowItemsMaxTotal,
|
||||||
specialFellowItemsAveTotal
|
specialFellowItemsAveTotal
|
||||||
} = storeToRefs(usePowerItems())
|
} = storeToRefs(usePowerItems())
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
if (standardFellowItems.value.keys().next().done) {
|
|
||||||
usePowerItems().fetchFellowItems()
|
|
||||||
}
|
|
||||||
if (specialFellowItems.value.keys().next().done) {
|
|
||||||
usePowerItems().fetchFellowItems()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-card class="flex-fill">
|
<v-card>
|
||||||
<v-card-title>Fellow Power</v-card-title>
|
<v-card-title>Fellow Power</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-sheet class="d-flex flex-wrap flex-fill">
|
<v-sheet class="d-flex flex-wrap flex-fill">
|
||||||
|
|
@ -43,7 +33,6 @@ onBeforeMount(() => {
|
||||||
:average-total="specialFellowItemsAveTotal"
|
:average-total="specialFellowItemsAveTotal"
|
||||||
/>
|
/>
|
||||||
<SummaryCard
|
<SummaryCard
|
||||||
item-type="Fellow"
|
|
||||||
class="ma-2 align-self-start"
|
class="ma-2 align-self-start"
|
||||||
:standard-total="standardFellowItemTotal"
|
:standard-total="standardFellowItemTotal"
|
||||||
:minimum-total="specialFellowItemsMinTotal"
|
:minimum-total="specialFellowItemsMinTotal"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import StandardItemsCard from '@/components/StandardItemsCard.vue'
|
||||||
import SummaryCard from '@/components/SummaryCard.vue'
|
import SummaryCard from '@/components/SummaryCard.vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { usePowerItems } from '@/stores/powerItems'
|
import { usePowerItems } from '@/stores/powerItems'
|
||||||
import { onBeforeMount } from 'vue'
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
standardIntimacyItems,
|
standardIntimacyItems,
|
||||||
|
|
@ -14,19 +13,10 @@ const {
|
||||||
specialIntimacyItemsMaxTotal,
|
specialIntimacyItemsMaxTotal,
|
||||||
specialIntimacyItemsAveTotal
|
specialIntimacyItemsAveTotal
|
||||||
} = storeToRefs(usePowerItems())
|
} = storeToRefs(usePowerItems())
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
if (standardIntimacyItems.value.keys().next().done) {
|
|
||||||
usePowerItems().fetchIntimacyItems()
|
|
||||||
}
|
|
||||||
if (specialIntimacyItems.value.keys().next().done) {
|
|
||||||
usePowerItems().fetchIntimacyItems()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-card class="flex-fill">
|
<v-card>
|
||||||
<v-card-title>Intimacy Power</v-card-title>
|
<v-card-title>Intimacy Power</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-sheet class="d-flex flex-wrap flex-fill">
|
<v-sheet class="d-flex flex-wrap flex-fill">
|
||||||
|
|
@ -43,7 +33,6 @@ onBeforeMount(() => {
|
||||||
:average-total="specialIntimacyItemsAveTotal"
|
:average-total="specialIntimacyItemsAveTotal"
|
||||||
/>
|
/>
|
||||||
<SummaryCard
|
<SummaryCard
|
||||||
item-type="Intimacy"
|
|
||||||
class="ma-2 align-self-start"
|
class="ma-2 align-self-start"
|
||||||
:standard-total="standardIntimacyItemTotal"
|
:standard-total="standardIntimacyItemTotal"
|
||||||
:minimum-total="specialIntimacyItemsMinTotal"
|
:minimum-total="specialIntimacyItemsMinTotal"
|
||||||
|
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="userinfo">
|
|
||||||
<div>
|
|
||||||
<h1>This is a login-protected page</h1>
|
|
||||||
<h2>
|
|
||||||
The following profile data is extended by information from ZITADELs userinfo endpoint.
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
<ul class="claims">
|
|
||||||
<li v-for="c in claims" :key="c.key">
|
|
||||||
<strong>{{ c.key }}</strong
|
|
||||||
>: {{ c.value }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<button @click="signout">Sign Out</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
@media (min-width: 1024px) {
|
|
||||||
.userinfo {
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { oidc, LOGIN_RETURN_URL_STORAGE } from '@/types/ConfigSymbols';
|
|
||||||
import { inject } from 'vue';
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
|
|
||||||
const zitadel = ref(inject(oidc))
|
|
||||||
const user = ref(zitadel.value?.userProfile)
|
|
||||||
const claims = computed(() => {
|
|
||||||
if (user.value !== undefined) {
|
|
||||||
return Object.keys(user.value).map((key) => ({
|
|
||||||
key,
|
|
||||||
value: (user.value as Oidc.Profile)[key]
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
})
|
|
||||||
|
|
||||||
const signout = function() {
|
|
||||||
if (user.value) {
|
|
||||||
zitadel.value?.signOut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(zitadel.value?.isAuthenticated)) {
|
|
||||||
let routePath = useRoute().path
|
|
||||||
if (routePath !== undefined && routePath !== null) {
|
|
||||||
sessionStorage.setItem(LOGIN_RETURN_URL_STORAGE, routePath.toString())
|
|
||||||
zitadel.value?.signIn()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="admin">
|
|
||||||
<div>
|
|
||||||
<h1>
|
|
||||||
Access denied
|
|
||||||
</h1>
|
|
||||||
<h2>
|
|
||||||
You don't have the role "admin" in your ZITADEL project.
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
@media (min-width: 1024px) {
|
|
||||||
.admin {
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { useGeneralState } from '@/stores/general'
|
|
||||||
|
|
||||||
console.log(useGeneralState().getLoginReturnUrl())
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>Hi mom</div>
|
|
||||||
</template>
|
|
||||||
52
src/views/Test.vue
Normal file
52
src/views/Test.vue
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import SpecialItemsCard from '@/components/SpecialItemsCard.vue'
|
||||||
|
import StandardItemsCard from '@/components/StandardItemsCard.vue'
|
||||||
|
import SummaryCard from '@/components/SummaryCard.vue'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { usePowerItems } from '@/stores/powerItems'
|
||||||
|
|
||||||
|
const {
|
||||||
|
standardBlessingItems,
|
||||||
|
standardBlessingItemTotal,
|
||||||
|
specialBlessingItems,
|
||||||
|
specialBlessingItemsMinTotal,
|
||||||
|
specialBlessingItemsMaxTotal,
|
||||||
|
specialBlessingItemsAveTotal
|
||||||
|
} = storeToRefs(usePowerItems())
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>Intimacy Power</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-sheet class="d-flex flex-wrap flex-fill">
|
||||||
|
<StandardItemsCard
|
||||||
|
class="ma-2 align-self-start"
|
||||||
|
:items="standardBlessingItems"
|
||||||
|
:total="standardBlessingItemTotal"
|
||||||
|
/>
|
||||||
|
<SpecialItemsCard
|
||||||
|
class="ma-2 align-self-start"
|
||||||
|
:items="specialBlessingItems"
|
||||||
|
:minimum-total="specialBlessingItemsMinTotal"
|
||||||
|
:maximum-total="specialBlessingItemsMaxTotal"
|
||||||
|
:average-total="specialBlessingItemsAveTotal"
|
||||||
|
/>
|
||||||
|
<SummaryCard
|
||||||
|
class="ma-2 align-self-start"
|
||||||
|
:standard-total="standardBlessingItemTotal"
|
||||||
|
:minimum-total="specialBlessingItemsMinTotal"
|
||||||
|
:maximum-total="specialBlessingItemsMaxTotal"
|
||||||
|
:average-total="specialBlessingItemsAveTotal"
|
||||||
|
/>
|
||||||
|
</v-sheet>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use '@/styles/settings.scss';
|
||||||
|
:deep(tbody) tr:nth-of-type(even) {
|
||||||
|
background-color: rgba(var(--v-theme-primary-darken-1), 0.25);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import NewItemForm from '@/components/NewItemForm.vue';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<v-sheet class="flex-fill">
|
|
||||||
<NewItemForm />
|
|
||||||
</v-sheet>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
</style>
|
|
||||||
|
|
@ -2,13 +2,11 @@ import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import vuetify from 'vite-plugin-vuetify'
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
vuetify(),
|
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue