forked from Kispi/Core
feat(core): add employer portal with mock data
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
c94bf147f5
commit
3e1437300f
13 changed files with 300 additions and 184 deletions
|
@ -162,88 +162,6 @@
|
|||
"deleteRule": null,
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "74ftooxenpeq14b",
|
||||
"name": "accounts",
|
||||
"type": "base",
|
||||
"system": false,
|
||||
"schema": [
|
||||
{
|
||||
"id": "as1gvc1r",
|
||||
"name": "accountNumber",
|
||||
"type": "text",
|
||||
"system": false,
|
||||
"required": true,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "vxeq20lk",
|
||||
"name": "firstName",
|
||||
"type": "text",
|
||||
"system": false,
|
||||
"required": true,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bsinpdk7",
|
||||
"name": "lastName",
|
||||
"type": "text",
|
||||
"system": false,
|
||||
"required": true,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "syfivtki",
|
||||
"name": "lastCheckIn",
|
||||
"type": "date",
|
||||
"system": false,
|
||||
"required": false,
|
||||
"options": {
|
||||
"min": "",
|
||||
"max": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "pkoynx7h",
|
||||
"name": "personalData",
|
||||
"type": "relation",
|
||||
"system": false,
|
||||
"required": false,
|
||||
"options": {
|
||||
"collectionId": "p0gixtx6jwria0d",
|
||||
"cascadeDelete": false,
|
||||
"minSelect": null,
|
||||
"maxSelect": 1,
|
||||
"displayFields": [
|
||||
"firstNameParent",
|
||||
"lastNameParent",
|
||||
"email"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
"CREATE UNIQUE INDEX `idx_mPdvrAQ` ON `accounts` (`accountNumber`)"
|
||||
],
|
||||
"listRule": "",
|
||||
"viewRule": null,
|
||||
"createRule": null,
|
||||
"updateRule": "@request.data.id = null && @request.data.accountNumber = null && @request.data.firstName = null && @request.data.lastName = null && @request.data.created = null && @request.data.updated = null",
|
||||
"deleteRule": null,
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "c3zz98wpbn7m6zw",
|
||||
"name": "accountsList",
|
||||
|
@ -358,5 +276,147 @@
|
|||
"updateRule": null,
|
||||
"deleteRule": null,
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "s854d2w72fvyl54",
|
||||
"name": "companies",
|
||||
"type": "auth",
|
||||
"system": false,
|
||||
"schema": [],
|
||||
"indexes": [],
|
||||
"listRule": null,
|
||||
"viewRule": null,
|
||||
"createRule": null,
|
||||
"updateRule": null,
|
||||
"deleteRule": null,
|
||||
"options": {
|
||||
"allowEmailAuth": true,
|
||||
"allowOAuth2Auth": true,
|
||||
"allowUsernameAuth": true,
|
||||
"exceptEmailDomains": null,
|
||||
"manageRule": null,
|
||||
"minPasswordLength": 8,
|
||||
"onlyEmailDomains": null,
|
||||
"requireEmail": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "74ftooxenpeq14b",
|
||||
"name": "accounts",
|
||||
"type": "base",
|
||||
"system": false,
|
||||
"schema": [
|
||||
{
|
||||
"id": "as1gvc1r",
|
||||
"name": "accountNumber",
|
||||
"type": "text",
|
||||
"system": false,
|
||||
"required": true,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "vxeq20lk",
|
||||
"name": "firstName",
|
||||
"type": "text",
|
||||
"system": false,
|
||||
"required": true,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bsinpdk7",
|
||||
"name": "lastName",
|
||||
"type": "text",
|
||||
"system": false,
|
||||
"required": true,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "syfivtki",
|
||||
"name": "lastCheckIn",
|
||||
"type": "date",
|
||||
"system": false,
|
||||
"required": false,
|
||||
"options": {
|
||||
"min": "",
|
||||
"max": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "pkoynx7h",
|
||||
"name": "personalData",
|
||||
"type": "relation",
|
||||
"system": false,
|
||||
"required": false,
|
||||
"options": {
|
||||
"collectionId": "p0gixtx6jwria0d",
|
||||
"cascadeDelete": false,
|
||||
"minSelect": null,
|
||||
"maxSelect": 1,
|
||||
"displayFields": [
|
||||
"firstNameParent",
|
||||
"lastNameParent",
|
||||
"email"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gwrzizpu",
|
||||
"name": "company",
|
||||
"type": "relation",
|
||||
"system": false,
|
||||
"required": true,
|
||||
"options": {
|
||||
"collectionId": "s854d2w72fvyl54",
|
||||
"cascadeDelete": false,
|
||||
"minSelect": null,
|
||||
"maxSelect": 1,
|
||||
"displayFields": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rtvjzpxw",
|
||||
"name": "shift",
|
||||
"type": "text",
|
||||
"system": false,
|
||||
"required": true,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ma5ckyk1",
|
||||
"name": "wageFactor",
|
||||
"type": "number",
|
||||
"system": false,
|
||||
"required": true,
|
||||
"options": {
|
||||
"min": 0,
|
||||
"max": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
"CREATE UNIQUE INDEX `idx_mPdvrAQ` ON `accounts` (`accountNumber`)"
|
||||
],
|
||||
"listRule": "",
|
||||
"viewRule": null,
|
||||
"createRule": null,
|
||||
"updateRule": "(@request.data.id = null && @request.data.accountNumber = null && @request.data.firstName = null && @request.data.lastName = null && @request.data.created = null && @request.data.updated = null && @request.data.company = null && @request.data.shift = null && @request.data.wageFactor = null) || (@request.data.id = null && @request.data.accountNumber = null && @request.data.firstName = null && @request.data.lastName = null && @request.data.created = null && @request.data.updated = null && @request.data.company = null && @request.data.shift = null && @request.auth.id = company.id)",
|
||||
"deleteRule": null,
|
||||
"options": {}
|
||||
}
|
||||
]
|
|
@ -21,6 +21,7 @@
|
|||
"vue-router": "4.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/canvas-confetti": "^1.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.60.1",
|
||||
|
|
|
@ -28,6 +28,9 @@ dependencies:
|
|||
version: 4.2.2(vue@3.3.4)
|
||||
|
||||
devDependencies:
|
||||
'@tailwindcss/container-queries':
|
||||
specifier: ^0.1.1
|
||||
version: 0.1.1(tailwindcss@3.3.2)
|
||||
'@tailwindcss/typography':
|
||||
specifier: ^0.5.9
|
||||
version: 0.5.9(tailwindcss@3.3.2)
|
||||
|
@ -460,6 +463,14 @@ packages:
|
|||
dev: true
|
||||
optional: true
|
||||
|
||||
/@tailwindcss/container-queries@0.1.1(tailwindcss@3.3.2):
|
||||
resolution: {integrity: sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==}
|
||||
peerDependencies:
|
||||
tailwindcss: '>=3.2.0'
|
||||
dependencies:
|
||||
tailwindcss: 3.3.2
|
||||
dev: true
|
||||
|
||||
/@tailwindcss/typography@0.5.9(tailwindcss@3.3.2):
|
||||
resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==}
|
||||
peerDependencies:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<div class="relative">
|
||||
<AtomInput
|
||||
ref="input"
|
||||
v-model="modelValue"
|
||||
type="number"
|
||||
class="pr-[5.5rem] text-right"
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<template>
|
||||
<div class="h-full overflow-x-auto rounded-lg">
|
||||
<div
|
||||
v-if="settings"
|
||||
class="h-full overflow-x-auto rounded-lg"
|
||||
>
|
||||
<table class="table-pin-rows table-zebra table">
|
||||
<thead class="uppercase">
|
||||
<tr>
|
||||
|
@ -39,6 +42,17 @@
|
|||
>
|
||||
<CurrencyDollarIcon class="h-6 w-6" />
|
||||
</RouterLink>
|
||||
<span
|
||||
v-if="header.type === TableHeaderType.WAGE"
|
||||
:for="contactModalId"
|
||||
class="flex items-center gap-2"
|
||||
@click="$emit('open-contact-modal', entry.id)"
|
||||
>
|
||||
<span>{{ CurrencyService.toString(entry[header.key] * settings.minWage) }} / h</span>
|
||||
<div class="btn-ghost btn-sm btn p-1">
|
||||
<PencilSquareIcon class="h-5 w-5" />
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -47,10 +61,14 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue';
|
||||
import { PropType, onMounted, ref } from 'vue';
|
||||
import { DateService } from '../../services/date.service';
|
||||
import { CurrencyService } from '../../services/currency.service';
|
||||
import { CurrencyDollarIcon, DocumentTextIcon } from '@heroicons/vue/24/outline';
|
||||
import { CurrencyDollarIcon, DocumentTextIcon, PencilSquareIcon } from '@heroicons/vue/24/outline';
|
||||
import { SettingsService } from '../../services/settings.service';
|
||||
import { SettingsResponse } from '../../types/pocketbase.types';
|
||||
|
||||
const settings = ref<SettingsResponse>();
|
||||
|
||||
defineProps({
|
||||
tableHeaders: {
|
||||
|
@ -70,6 +88,10 @@ defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
settings.value = await SettingsService.getSettings();
|
||||
});
|
||||
|
||||
defineEmits(['open-contact-modal']);
|
||||
</script>
|
||||
|
||||
|
@ -81,6 +103,7 @@ export enum TableHeaderType {
|
|||
DATETIME,
|
||||
BUTTON_CONTACT,
|
||||
BUTTON_ACCOUNT,
|
||||
WAGE,
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<div class="flex place-items-center justify-between">
|
||||
<span>{{ inputLabel }}</span>
|
||||
<AtomCurrencyInput
|
||||
ref="input"
|
||||
v-model="amount"
|
||||
class="w-4/12"
|
||||
@keyup.enter="handleSubmit()"
|
||||
@keyup.esc="abortButton.click()"
|
||||
|
@ -39,7 +39,7 @@ import { CheckCircleIcon, XCircleIcon } from '@heroicons/vue/24/outline';
|
|||
import AtomCurrencyInput from '../atoms/AtomCurrencyInput.vue';
|
||||
import AtomModal from '../atoms/AtomModal.vue';
|
||||
|
||||
const input = ref();
|
||||
const amount = ref();
|
||||
const abortButton = ref();
|
||||
|
||||
defineProps({
|
||||
|
@ -66,16 +66,16 @@ const emit = defineEmits(['submit']);
|
|||
function handleOpen(event: {id: string, open: boolean}) {
|
||||
if(event.open) {
|
||||
// This is (currently) the only method to focus a input field inside a custom component.
|
||||
input.value.$refs.input.$refs.input.value = '';
|
||||
input.value.$refs.input.$refs.input.focus();
|
||||
// input.value.$el.$refs.input.$refs.input.value = '';
|
||||
// input.value.$el.$refs.input.$refs.input.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
if(input.value.$refs.input.$refs.input.value <= 0 || input.value.$refs.input.$refs.input.value > 100) {
|
||||
if(amount.value <= 0) {
|
||||
return;
|
||||
}
|
||||
emit('submit', input.value.$refs.input.$refs.input.value);
|
||||
emit('submit', amount.value);
|
||||
abortButton.value.click();
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -30,19 +30,24 @@ const initAccounts = ref<AccountsListResponse<number, string>[]>([]);
|
|||
const searchQuery = ref('');
|
||||
const tableHeaders = [
|
||||
{
|
||||
title: 'Name',
|
||||
key: 'name',
|
||||
title: 'Vorname',
|
||||
key: 'firstName',
|
||||
type: TableHeaderType.STRING,
|
||||
},
|
||||
{
|
||||
title: 'Nachname',
|
||||
key: 'lastName',
|
||||
type: TableHeaderType.STRING,
|
||||
},
|
||||
{
|
||||
title: 'Schicht',
|
||||
key: 'lastCheckIn',
|
||||
type: TableHeaderType.DATETIME,
|
||||
key: 'shift',
|
||||
type: TableHeaderType.STRING,
|
||||
},
|
||||
{
|
||||
title: 'Lohn',
|
||||
key: 'balance',
|
||||
type: TableHeaderType.CURRENCY,
|
||||
key: 'wageFactor',
|
||||
type: TableHeaderType.WAGE,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -54,7 +59,7 @@ useEventBus<boolean>('isAuthenticated').on(state => {
|
|||
});
|
||||
|
||||
async function getData() {
|
||||
initAccounts.value = await AccountService.getAccounts();
|
||||
initAccounts.value = await AccountService.getAccountsByCompanyId('c09ncb3l2pi6i0u');
|
||||
accounts.value = initAccounts.value;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<OrganismAuthWrapper class="flex h-full flex-wrap gap-4 p-6">
|
||||
<div class="@container">
|
||||
<OrganismAuthWrapper class="flex h-full flex-wrap justify-center gap-4 p-6 @[832px]:justify-normal">
|
||||
<AtomCard class="w-96">
|
||||
<template #title>
|
||||
<WrenchScrewdriverIcon class="h-6 w-6" />Einstellungen
|
||||
|
@ -84,6 +85,7 @@
|
|||
</div>
|
||||
</AtomCard>
|
||||
</OrganismAuthWrapper>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
@ -115,9 +117,10 @@ const settingsSaveError = ref<boolean>(false);
|
|||
|
||||
onMounted(async () => {
|
||||
settings.value = await SettingsService.getSettings();
|
||||
minWage.value = settings.value.minWage;
|
||||
minWage.value = settings.value.minWage / 100;
|
||||
incomeTax.value = settings.value.incomeTax;
|
||||
maxWageFactor.value = settings.value.maxWageFactor;
|
||||
radioUrl.value = settings.value.radioUrl;
|
||||
});
|
||||
|
||||
async function saveSettings() {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import { RecordSubscription, UnsubscribeFunc } from 'pocketbase';
|
||||
import { AccountsDataResponse, AccountsListResponse, AccountsResponse, Collections } from '../types/pocketbase.types';
|
||||
import { PocketbaseService } from './pocketbase.service';
|
||||
|
@ -20,6 +19,9 @@ export class AccountService {
|
|||
public static getAccountDetails(id: string): Promise<AccountsResponse<{personalData: AccountsDataResponse}>> {
|
||||
return COLLECTION.getOne(id, { expand: 'personalData' });
|
||||
}
|
||||
public static getAccountsByCompanyId(companyId: string): Promise<AccountsListResponse<number, string>[]> {
|
||||
return COLLECTION.getFullList({ filter: `company.id = "${companyId}"` });
|
||||
}
|
||||
public static async subscribeToAccountChanges(
|
||||
callback: (data: RecordSubscription<AccountsResponse>)=> void,
|
||||
): Promise<UnsubscribeFunc> {
|
||||
|
|
|
@ -3,12 +3,12 @@ export class CurrencyService {
|
|||
return `${(value / 100).toLocaleString('de-DE', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})} Öro`;
|
||||
})} Batzen`;
|
||||
}
|
||||
public static toSignedString(value: number): string {
|
||||
return `${(value / 100) > 0 ? '+' : ''}${(value / 100).toLocaleString('de', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})} Öro`;
|
||||
})} Batzen`;
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ export class SettingsService {
|
|||
return COLLECTION.getFirstListItem('', { sort: '-created' });
|
||||
}
|
||||
public static async setSettings(settings: SettingsRecord): Promise<SettingsResponse> {
|
||||
settings.minWage = settings.minWage * 100;
|
||||
return COLLECTION.create(settings);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ export enum Collections {
|
|||
Accounts = "accounts",
|
||||
AccountsData = "accountsData",
|
||||
AccountsList = "accountsList",
|
||||
Companies = "companies",
|
||||
Settings = "settings",
|
||||
Transactions = "transactions",
|
||||
}
|
||||
|
@ -40,6 +41,9 @@ export type AccountsRecord = {
|
|||
lastName: string
|
||||
lastCheckIn?: IsoDateString
|
||||
personalData?: RecordIdString
|
||||
company: RecordIdString
|
||||
shift: string
|
||||
wageFactor: number
|
||||
}
|
||||
|
||||
export type AccountsDataRecord = {
|
||||
|
@ -60,6 +64,8 @@ export type AccountsListRecord<Tbalance = unknown, Tname = unknown> = {
|
|||
balance?: null | Tbalance
|
||||
}
|
||||
|
||||
export type CompaniesRecord = never
|
||||
|
||||
export type SettingsRecord = {
|
||||
minWage: number
|
||||
incomeTax: number
|
||||
|
@ -77,6 +83,7 @@ export type TransactionsRecord = {
|
|||
export type AccountsResponse<Texpand = unknown> = Required<AccountsRecord> & BaseSystemFields<Texpand>
|
||||
export type AccountsDataResponse<Texpand = unknown> = Required<AccountsDataRecord> & BaseSystemFields<Texpand>
|
||||
export type AccountsListResponse<Tbalance = unknown, Tname = unknown, Texpand = unknown> = Required<AccountsListRecord<Tbalance, Tname>> & BaseSystemFields<Texpand>
|
||||
export type CompaniesResponse<Texpand = unknown> = Required<CompaniesRecord> & AuthSystemFields<Texpand>
|
||||
export type SettingsResponse<Texpand = unknown> = Required<SettingsRecord> & BaseSystemFields<Texpand>
|
||||
export type TransactionsResponse<Texpand = unknown> = Required<TransactionsRecord> & BaseSystemFields<Texpand>
|
||||
|
||||
|
@ -86,6 +93,7 @@ export type CollectionRecords = {
|
|||
accounts: AccountsRecord
|
||||
accountsData: AccountsDataRecord
|
||||
accountsList: AccountsListRecord
|
||||
companies: CompaniesRecord
|
||||
settings: SettingsRecord
|
||||
transactions: TransactionsRecord
|
||||
}
|
||||
|
@ -94,6 +102,7 @@ export type CollectionResponses = {
|
|||
accounts: AccountsResponse
|
||||
accountsData: AccountsDataResponse
|
||||
accountsList: AccountsListResponse
|
||||
companies: CompaniesResponse
|
||||
settings: SettingsResponse
|
||||
transactions: TransactionsResponse
|
||||
}
|
|
@ -6,7 +6,7 @@ module.exports = {
|
|||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [require('@tailwindcss/typography'), require('daisyui')],
|
||||
plugins: [require('@tailwindcss/typography'), require('daisyui'), require('@tailwindcss/container-queries')],
|
||||
daisyui: {
|
||||
logs: false,
|
||||
themes: [
|
||||
|
|
Loading…
Reference in a new issue