Added Teams
Minor Rework of Plantafel
This commit is contained in:
@@ -7,8 +7,13 @@ const { $api } = useNuxtApp()
|
||||
const id = route.params.id as string
|
||||
const profile = ref<any>(null)
|
||||
const branches = ref<any[]>([])
|
||||
const teams = ref<any[]>([])
|
||||
const pending = ref(true)
|
||||
const saving = ref(false)
|
||||
const selectMenuUi = {
|
||||
base: 'w-full',
|
||||
content: 'min-w-[min(32rem,90vw)] w-max max-w-[90vw]'
|
||||
}
|
||||
|
||||
async function fetchBranches() {
|
||||
try {
|
||||
@@ -19,6 +24,15 @@ async function fetchBranches() {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchTeams() {
|
||||
try {
|
||||
teams.value = await useEntities("teams").select()
|
||||
} catch (err) {
|
||||
console.error('[fetchTeams]', err)
|
||||
teams.value = []
|
||||
}
|
||||
}
|
||||
|
||||
/** Profil laden **/
|
||||
async function fetchProfile() {
|
||||
pending.value = true
|
||||
@@ -26,6 +40,7 @@ async function fetchProfile() {
|
||||
profile.value = await $api(`/api/profiles/${id}`)
|
||||
ensureWorkingHoursStructure()
|
||||
ensureBranchStructure()
|
||||
ensureTeamStructure()
|
||||
} catch (err: any) {
|
||||
console.error('[fetchProfile]', err)
|
||||
toast.add({
|
||||
@@ -58,6 +73,20 @@ function ensureBranchStructure() {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureTeamStructure() {
|
||||
if (!profile.value) return
|
||||
|
||||
if (!Array.isArray(profile.value.team_ids)) {
|
||||
if (Array.isArray(profile.value.teams)) {
|
||||
profile.value.team_ids = profile.value.teams
|
||||
.map((entry: any) => entry?.id ?? entry)
|
||||
.filter((entry: any) => entry != null)
|
||||
} else {
|
||||
profile.value.team_ids = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updatePrimaryBranch = (value: number | null) => {
|
||||
if (!profile.value) return
|
||||
profile.value.branch_id = value
|
||||
@@ -77,6 +106,11 @@ const updateBranchMemberships = (values: number[]) => {
|
||||
}
|
||||
}
|
||||
|
||||
const updateTeamMemberships = (values: number[]) => {
|
||||
if (!profile.value) return
|
||||
profile.value.team_ids = values || []
|
||||
}
|
||||
|
||||
/** Profil speichern **/
|
||||
async function saveProfile() {
|
||||
if (saving.value) return
|
||||
@@ -180,7 +214,7 @@ const checkZip = async () => {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([fetchBranches(), fetchProfile()])
|
||||
await Promise.all([fetchBranches(), fetchTeams(), fetchProfile()])
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -234,29 +268,29 @@ onMounted(async () => {
|
||||
<USeparator label="Persönliche Daten" />
|
||||
|
||||
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||
<UFormField label="Vorname">
|
||||
<UInput v-model="profile.first_name" />
|
||||
<UFormField label="Vorname" class="w-full">
|
||||
<UInput v-model="profile.first_name" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Nachname">
|
||||
<UInput v-model="profile.last_name" />
|
||||
<UFormField label="Nachname" class="w-full">
|
||||
<UInput v-model="profile.last_name" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="E-Mail">
|
||||
<UInput v-model="profile.email" />
|
||||
<UFormField label="E-Mail" class="w-full">
|
||||
<UInput v-model="profile.email" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Telefon (Mobil)">
|
||||
<UInput v-model="profile.mobile_tel" />
|
||||
<UFormField label="Telefon (Mobil)" class="w-full">
|
||||
<UInput v-model="profile.mobile_tel" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Telefon (Festnetz)">
|
||||
<UInput v-model="profile.fixed_tel" />
|
||||
<UFormField label="Telefon (Festnetz)" class="w-full">
|
||||
<UInput v-model="profile.fixed_tel" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Geburtstag">
|
||||
<UFormField label="Geburtstag" class="w-full">
|
||||
<div class="flex items-center gap-2">
|
||||
<UInput type="date" v-model="profile.birthday" class="flex-1" />
|
||||
<UInput type="date" v-model="profile.birthday" class="flex-1 w-full" />
|
||||
<UButton color="gray" variant="soft" label="Heute" @click="setProfileDate('birthday')" />
|
||||
</div>
|
||||
</UFormField>
|
||||
@@ -266,37 +300,37 @@ onMounted(async () => {
|
||||
<USeparator label="Vertragsinformationen" />
|
||||
|
||||
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||
<UFormField label="Vertragsart">
|
||||
<UInput v-model="profile.contract_type"/>
|
||||
<UFormField label="Vertragsart" class="w-full">
|
||||
<UInput v-model="profile.contract_type" class="w-full"/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Status">
|
||||
<UInput v-model="profile.status"/>
|
||||
<UFormField label="Status" class="w-full">
|
||||
<UInput v-model="profile.status" class="w-full"/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Position">
|
||||
<UInput v-model="profile.position"/>
|
||||
<UFormField label="Position" class="w-full">
|
||||
<UInput v-model="profile.position" class="w-full"/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Qualifikation">
|
||||
<UInput v-model="profile.qualification"/>
|
||||
<UFormField label="Qualifikation" class="w-full">
|
||||
<UInput v-model="profile.qualification" class="w-full"/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Eintrittsdatum">
|
||||
<UFormField label="Eintrittsdatum" class="w-full">
|
||||
<div class="flex items-center gap-2">
|
||||
<UInput type="date" v-model="profile.entry_date" class="flex-1" />
|
||||
<UInput type="date" v-model="profile.entry_date" class="flex-1 w-full" />
|
||||
<UButton color="gray" variant="soft" label="Heute" @click="setProfileDate('entry_date')" />
|
||||
</div>
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Wöchentliche Arbeitszeit (Std)">
|
||||
<UInput type="number" v-model="profile.weekly_working_hours" />
|
||||
<UFormField label="Wöchentliche Arbeitszeit (Std)" class="w-full">
|
||||
<UInput type="number" v-model="profile.weekly_working_hours" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Bezahlte Urlaubstage (Jahr)">
|
||||
<UInput type="number" v-model="profile.annual_paid_leave_days" />
|
||||
<UFormField label="Bezahlte Urlaubstage (Jahr)" class="w-full">
|
||||
<UInput type="number" v-model="profile.annual_paid_leave_days" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Aktiv">
|
||||
<UFormField label="Aktiv" class="w-full">
|
||||
<div class="flex items-center gap-3">
|
||||
<USwitch v-model="profile.active" color="primary" />
|
||||
<span class="text-sm text-gray-600">
|
||||
@@ -311,52 +345,83 @@ onMounted(async () => {
|
||||
<USeparator label="Niederlassungen" />
|
||||
|
||||
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||
<UFormField label="Primäre Niederlassung">
|
||||
<UFormField label="Primäre Niederlassung" class="w-full">
|
||||
<USelectMenu
|
||||
:model-value="profile.branch_id"
|
||||
:items="branches"
|
||||
label-key="name"
|
||||
value-key="id"
|
||||
class="w-full"
|
||||
:ui="selectMenuUi"
|
||||
@update:model-value="updatePrimaryBranch"
|
||||
/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Weitere Niederlassungen">
|
||||
<UFormField label="Weitere Niederlassungen" class="w-full">
|
||||
<USelectMenu
|
||||
:model-value="profile.branch_ids"
|
||||
:items="branches"
|
||||
label-key="name"
|
||||
value-key="id"
|
||||
multiple
|
||||
class="w-full"
|
||||
:ui="selectMenuUi"
|
||||
@update:model-value="updateBranchMemberships"
|
||||
/>
|
||||
</UFormField>
|
||||
</UForm>
|
||||
</UCard>
|
||||
|
||||
<UCard v-if="!pending && profile" class="mt-3">
|
||||
<USeparator label="Teams" />
|
||||
|
||||
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||
<UFormField label="Team-Zuordnung" class="w-full">
|
||||
<USelectMenu
|
||||
:model-value="profile.team_ids"
|
||||
:items="teams"
|
||||
label-key="name"
|
||||
value-key="id"
|
||||
multiple
|
||||
class="w-full"
|
||||
:ui="selectMenuUi"
|
||||
@update:model-value="updateTeamMemberships"
|
||||
/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Hinweis" class="w-full">
|
||||
<div class="text-sm text-gray-500 pt-2">
|
||||
Teams können in den Stammdaten gepflegt und optional einer Niederlassung zugeordnet werden.
|
||||
</div>
|
||||
</UFormField>
|
||||
</UForm>
|
||||
</UCard>
|
||||
|
||||
<UCard v-if="!pending && profile" class="mt-3">
|
||||
<USeparator label="Adresse & Standort" />
|
||||
|
||||
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||
<UFormField label="Straße und Hausnummer">
|
||||
<UInput v-model="profile.address_street"/>
|
||||
<UFormField label="Straße und Hausnummer" class="w-full">
|
||||
<UInput v-model="profile.address_street" class="w-full"/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="PLZ">
|
||||
<UInput type="text" v-model="profile.address_zip" @focusout="checkZip"/>
|
||||
<UFormField label="PLZ" class="w-full">
|
||||
<UInput type="text" v-model="profile.address_zip" class="w-full" @focusout="checkZip"/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Ort">
|
||||
<UInput v-model="profile.address_city"/>
|
||||
<UFormField label="Ort" class="w-full">
|
||||
<UInput v-model="profile.address_city" class="w-full"/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Bundesland">
|
||||
<UFormField label="Bundesland" class="w-full">
|
||||
<USelectMenu
|
||||
v-model="profile.state_code"
|
||||
:options="bundeslaender"
|
||||
value-attribute="code"
|
||||
option-attribute="name"
|
||||
placeholder="Bundesland auswählen"
|
||||
class="w-full"
|
||||
:ui="selectMenuUi"
|
||||
/>
|
||||
</UFormField>
|
||||
</UForm>
|
||||
@@ -394,20 +459,20 @@ onMounted(async () => {
|
||||
<UCard v-if="!pending && profile" class="mt-3">
|
||||
<USeparator label="Sonstiges" />
|
||||
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||
<UFormField label="Kleidergröße (Oberteil)">
|
||||
<UInput v-model="profile.clothing_size_top" />
|
||||
<UFormField label="Kleidergröße (Oberteil)" class="w-full">
|
||||
<UInput v-model="profile.clothing_size_top" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Kleidergröße (Hose)">
|
||||
<UInput v-model="profile.clothing_size_bottom" />
|
||||
<UFormField label="Kleidergröße (Hose)" class="w-full">
|
||||
<UInput v-model="profile.clothing_size_bottom" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Schuhgröße">
|
||||
<UInput v-model="profile.clothing_size_shoe" />
|
||||
<UFormField label="Schuhgröße" class="w-full">
|
||||
<UInput v-model="profile.clothing_size_shoe" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Token-ID">
|
||||
<UInput v-model="profile.token_id" />
|
||||
<UFormField label="Token-ID" class="w-full">
|
||||
<UInput v-model="profile.token_id" class="w-full" />
|
||||
</UFormField>
|
||||
</UForm>
|
||||
</UCard>
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
employee_number: profile?.employee_number || '',
|
||||
full_name: profile?.full_name || user?.full_name || user?.email || 'Ohne Profil',
|
||||
email: user?.email || profile?.email || '',
|
||||
branch_name: profile?.branch?.name || ''
|
||||
branch_name: profile?.branch?.name || '',
|
||||
team_names: (profile?.teams || []).map((team) => team?.name).filter(Boolean).join(', ')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +53,9 @@
|
||||
},{
|
||||
key: "branch_name",
|
||||
label: "Niederlassung",
|
||||
},{
|
||||
key: "team_names",
|
||||
label: "Teams",
|
||||
}
|
||||
]
|
||||
const selectedColumns = ref(templateColumns)
|
||||
|
||||
Reference in New Issue
Block a user