Add more editable profile fields

This commit is contained in:
Daniel Egger 2024-07-16 10:58:58 +02:00
parent 47896444a6
commit d56c346512
5 changed files with 121 additions and 35 deletions

View File

@ -3,6 +3,8 @@ import { useEntities } from "@/services/entities";
import AvatarImage from "@/components/ui/AvatarImage.vue";
import { ref } from "vue";
import { type User, useUserStore } from "@/stores/user";
import ItDatePicker from "@/components/ui/ItDatePicker.vue";
import { normalizeSwissPhoneNumber } from "@/utils/phone";
const emit = defineEmits(["cancel", "save"]);
@ -26,6 +28,7 @@ const formData = ref({
birth_date: user.birth_date,
organisation: user.organisation,
organisation_detail_name: user.organisation_detail_name,
organisation_street: user.organisation_street,
organisation_street_number: user.organisation_street_number,
organisation_postal_code: user.organisation_postal_code,
@ -35,7 +38,8 @@ const formData = ref({
});
async function save() {
const { country_code, organisation_country_code, ...profileData } = formData.value;
const { country_code, organisation_country_code, phone_number, ...profileData } =
formData.value;
const typedProfileData: Partial<User> = { ...profileData };
typedProfileData.country = countries.value.find(
@ -45,6 +49,10 @@ async function save() {
(c) => c.country_code === organisation_country_code
);
if (phone_number) {
typedProfileData.phone_number = normalizeSwissPhoneNumber(phone_number);
}
await user.updateUserProfile(typedProfileData);
emit("save");
}
@ -130,6 +138,28 @@ async function avatarUpload(e: Event) {
disabled
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6"
/>
<label for="phone" class="block pb-1.5 leading-6">
{{ $t("a.Telefonnummer") }}
</label>
<div>
<input
id="phone"
v-model="formData.phone_number"
type="text"
name="phone"
autocomplete="phone-number"
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6"
/>
</div>
<label for="birth-date" class="block pb-1.5 leading-6">
{{ $t("a.Geburtsdatum") }}
</label>
<div class="mb-4 block w-full py-1.5 sm:max-w-sm sm:text-sm sm:leading-6">
<ItDatePicker v-model="formData.birth_date"></ItDatePicker>
</div>
<label class="block pb-1.5 leading-6">
{{ $t("a.Profilbild") }}
</label>
@ -268,6 +298,22 @@ async function avatarUpload(e: Event) {
{{ $t("a.Firmenanschrift") }}
</h4>
<div class="flex flex-col justify-start md:flex-row md:space-x-4">
<div class="w-full md:max-w-lg">
<label for="org-street-address" class="block pb-1.5 leading-6">
{{ $t("a.Name") }}
</label>
<input
id="org-detail-name"
v-model="formData.organisation_detail_name"
type="text"
name="org-detail-name"
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div class="flex flex-col justify-start md:flex-row md:space-x-4">
<div class="w-full md:max-w-sm">
<label for="org-street-address" class="block pb-1.5 leading-6">

View File

@ -3,6 +3,8 @@ import { useUserStore } from "@/stores/user";
import { computed } from "vue";
import { useEntities } from "@/services/entities";
import { useTranslation } from "i18next-vue";
import dayjs from "dayjs";
import { displaySwissPhoneNumber } from "@/utils/phone";
const { t } = useTranslation();
@ -10,21 +12,20 @@ const user = useUserStore();
const { organisations } = useEntities();
const privateAddress = computed(() => {
let addressText = `${user.street} ${user.street_number}`.trim();
if (user.postal_code || user.city) {
if (addressText.length) {
addressText += ", ";
}
addressText += `${user.postal_code} ${user.city}`;
}
if (user.country) {
if (addressText.length) {
addressText += ", ";
}
addressText += user.country.name;
const textParts = [];
if (user.street || user.street_number) {
textParts.push(`${user.street} ${user.street_number}`.trim());
}
return addressText.trim();
if (user.postal_code || user.city) {
textParts.push(`${user.postal_code} ${user.city}`);
}
if (textParts.length && user.country) {
textParts.push(user.country.name);
}
return textParts;
});
const organisationName = computed(() => {
@ -36,22 +37,25 @@ const organisationName = computed(() => {
});
const orgAddress = computed(() => {
let addressText =
`${user.organisation_street} ${user.organisation_street_number}`.trim();
if (user.organisation_postal_code || user.organisation_city) {
if (addressText.length) {
addressText += ", ";
}
addressText += `${user.organisation_postal_code} ${user.organisation_city}`;
}
if (user.organisation_country) {
if (addressText.length) {
addressText += ", ";
}
addressText += user.organisation_country.name;
const textParts = [];
if (user.organisation_detail_name) {
textParts.push(user.organisation_detail_name);
}
return addressText.trim();
if (user.organisation_street || user.organisation_street_number) {
textParts.push(
`${user.organisation_street} ${user.organisation_street_number}`.trim()
);
}
if (user.organisation_postal_code || user.organisation_city) {
textParts.push(`${user.organisation_postal_code} ${user.organisation_city}`);
}
if (textParts.length && user.organisation_country) {
textParts.push(user.organisation_country.name);
}
return textParts;
});
const invoiceAddress = computed(() => {
@ -74,13 +78,34 @@ const invoiceAddress = computed(() => {
{{ $t("a.E-Mail Adresse") }}
</label>
<div class="mb-3 sm:col-span-2 sm:mb-0">{{ user.email }}</div>
<label class="block font-semibold leading-6">
{{ $t("a.Telefonnummer") }}
</label>
<div class="mb-3 sm:col-span-2 sm:mb-0">
<span v-if="user.phone_number">
{{ displaySwissPhoneNumber(user.phone_number) }}
</span>
<span v-else class="text-gray-800">{{ $t("a.Keine Angabe") }}</span>
</div>
<label class="block font-semibold leading-6">
{{ $t("a.Geburtsdatum") }}
</label>
<div class="mb-3 sm:col-span-2 sm:mb-0">
<span v-if="user.birth_date">
{{ dayjs(user.birth_date).format("DD.MM.YYYY") }}
</span>
<span v-else class="text-gray-800">{{ $t("a.Keine Angabe") }}</span>
</div>
<label class="block font-semibold leading-6">
{{ $t("a.Privatadresse") }}
</label>
<div class="mb-3 sm:col-span-2 sm:mb-0">
<template v-if="privateAddress">
{{ privateAddress }}
</template>
<div v-if="privateAddress.length">
<span v-for="(line, index) in privateAddress" :key="index">
{{ line }}
<br />
</span>
</div>
<span v-else class="text-gray-800">{{ $t("a.Keine Angabe") }}</span>
</div>
</div>
@ -94,9 +119,13 @@ const invoiceAddress = computed(() => {
{{ $t("a.Firmenanschrift") }}
</label>
<div class="sm:col-span-2">
<template v-if="orgAddress">
{{ orgAddress }}
</template>
<!-- eslint-disable vue/no-v-html -->
<div v-if="orgAddress">
<span v-for="(line, index) in orgAddress" :key="index">
{{ line }}
<br />
</span>
</div>
<span v-else class="text-gray-800">{{ $t("a.Keine Angabe") }}</span>
</div>
</div>

View File

@ -1,6 +1,7 @@
<script setup lang="ts">
import { useUserStore } from "@/stores/user";
import VueDatePicker from "@vuepic/vue-datepicker";
import "@vuepic/vue-datepicker/dist/main.css";
import { defineProps, withDefaults, defineModel } from "vue";
const model = defineModel<string>();

View File

@ -13,6 +13,8 @@ import VerticalBarChart from "@/components/ui/VerticalBarChart.vue";
import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
import logger from "loglevel";
import { reactive, ref } from "vue";
import VueDatePicker from "@vuepic/vue-datepicker";
import "@vuepic/vue-datepicker/dist/main.css";
const state = reactive({
checkboxValue: true,
@ -34,6 +36,8 @@ const state = reactive({
},
});
const birtDate = ref("1982-06-15");
const dropdownData = [
{
title: "Option 1",
@ -411,6 +415,11 @@ function log(data: any) {
></ItDropdownSelect>
{{ state.dropdownSelected }}
<h2 class="mb-8 mt-8">Date Picker</h2>
<div class="mt-2">
<VueDatePicker v-model="birthDate"></VueDatePicker>
</div>
<h2 class="mb-8 mt-8">Checkbox</h2>
<ItCheckbox

View File

@ -114,8 +114,9 @@ class User(AbstractUser):
blank=True,
)
# fields gathered from cembra pay form
birth_date = models.DateField(null=True, blank=True)
# phone number should be stored in the format +41792018586 (not validated)
phone_number = models.CharField(max_length=255, blank=True, default="")
# is only set by abacus invoice export code