1
0
Fork 0
forked from Kispi/Core

feat(core): add employer portal with mock data
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Simon Giesel 2023-06-30 16:18:07 +02:00
parent c94bf147f5
commit 3e1437300f
13 changed files with 300 additions and 184 deletions

View file

@ -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": {}
}
]

View file

@ -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",

View file

@ -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:

View file

@ -1,6 +1,7 @@
<template>
<div class="relative">
<AtomInput
ref="input"
v-model="modelValue"
type="number"
class="pr-[5.5rem] text-right"

View file

@ -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>

View file

@ -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>

View file

@ -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;
}

View file

@ -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() {

View file

@ -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> {

View file

@ -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`;
}
}

View file

@ -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);
}
}

View file

@ -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
}

View file

@ -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: [