diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 909e9bdb..0dcb4cc1 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -97,6 +97,7 @@ js-linting: &js-linting default-steps: &default-steps - parallel: + - step: *e2e - step: *e2e - step: *e2e - step: *python-tests diff --git a/caprover_create_app.py b/caprover_create_app.py index 24dba123..e137bcf1 100644 --- a/caprover_create_app.py +++ b/caprover_create_app.py @@ -93,8 +93,12 @@ def main(app_name, image_name, environment_file): "AWS_S3_SECRET_ACCESS_KEY": env.str("AWS_S3_SECRET_ACCESS_KEY", ""), "AWS_S3_REGION_NAME": "eu-central-1", "AWS_STORAGE_BUCKET_NAME": "myvbv-dev.iterativ.ch", - "DATATRANS_HMAC_KEY": env.str("DATATRANS_HMAC_KEY", ""), - "DATATRANS_BASIC_AUTH_KEY": env.str("DATATRANS_BASIC_AUTH_KEY", ""), + "DATATRANS_HMAC_KEY": env.str("PIPELINES_DATATRANS_HMAC_KEY", ""), + "DATATRANS_BASIC_AUTH_KEY": env.str( + "PIPELINES_DATATRANS_BASIC_AUTH_KEY", "" + ), + "DATATRANS_API_ENDPOINT": "https://api.sandbox.datatrans.com", + "DATATRANS_PAY_URL": "https://pay.sandbox.datatrans.com", "FILE_UPLOAD_STORAGE": "s3", "IT_DJANGO_DEBUG": "false", "IT_SERVE_VUE": "false", diff --git a/client/src/components/dashboard/UkStatistics.vue b/client/src/components/dashboard/UkStatistics.vue index 7d70d930..720ce3e2 100644 --- a/client/src/components/dashboard/UkStatistics.vue +++ b/client/src/components/dashboard/UkStatistics.vue @@ -6,6 +6,7 @@ import type { CourseStatisticsType } from "@/gql/graphql"; import AssignmentSummaryBox from "@/components/dashboard/AssignmentSummaryBox.vue"; import FeedbackSummaryBox from "@/components/dashboard/FeedbackSummaryBox.vue"; import CompetenceSummaryBox from "@/components/dashboard/CompetenceSummaryBox.vue"; +import LoadingSpinner from "@/components/ui/LoadingSpinner.vue"; const props = defineProps<{ courseId: string; @@ -95,4 +96,7 @@ onMounted(async () => { /> +
+ +
diff --git a/client/src/components/learningPath/LearningPathDiagram.vue b/client/src/components/learningPath/LearningPathDiagram.vue index ce0c098e..e91aae0c 100644 --- a/client/src/components/learningPath/LearningPathDiagram.vue +++ b/client/src/components/learningPath/LearningPathDiagram.vue @@ -3,6 +3,7 @@ import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPa import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils"; import { computed } from "vue"; import { useCourseCircleProgress, useCourseDataWithCompletion } from "@/composables"; +import LoadingSpinner from "@/components/ui/LoadingSpinner.vue"; export type DiagramType = "horizontal" | "horizontalSmall" | "singleSmall"; @@ -54,23 +55,25 @@ const { inProgressCirclesCount, circlesCount } = useCourseCircleProgress( diff --git a/client/src/components/onboarding/OrganisationAddress.vue b/client/src/components/onboarding/OrganisationAddress.vue index b8b59889..e56e33cd 100644 --- a/client/src/components/onboarding/OrganisationAddress.vue +++ b/client/src/components/onboarding/OrganisationAddress.vue @@ -34,7 +34,7 @@ const orgAddress = computed({ for="company-name" class="block text-sm font-medium leading-6 text-gray-900" > - {{ $t("a.Name") }} + {{ $t("a.Firmenname") }}
import { computed } from "vue"; import { useEntities } from "@/services/entities"; -import VueDatePicker from "@vuepic/vue-datepicker"; import "@vuepic/vue-datepicker/dist/main.css"; import { t } from "i18next"; -import { useUserStore } from "@/stores/user"; +import ItDatePicker from "@/components/ui/ItDatePicker.vue"; const props = defineProps<{ modelValue: { @@ -25,20 +24,12 @@ const props = defineProps<{ const emit = defineEmits(["update:modelValue"]); const { countries } = useEntities(); -const userStore = useUserStore(); const paymentMethods = [ { value: "credit_card", label: t("a.Debit-/Kreditkarte/Twint") }, { value: "cembra_byjuno", label: t("a.Rechnung") }, ]; -// TODO: remove after cembra is ready for production -const appEnv = import.meta.env.VITE_APP_ENVIRONMENT || "local"; -if (appEnv.startsWith("prod")) { - paymentMethods.splice(1, 1); -} -// END TODO - const address = computed({ get() { return props.modelValue; @@ -234,40 +225,8 @@ const address = computed({ {{ $t("a.Geburtsdatum") }}
- +
- - diff --git a/client/src/components/personalProfile/ProfileEdit.vue b/client/src/components/personalProfile/ProfileEdit.vue index 77d0da8d..398e363a 100644 --- a/client/src/components/personalProfile/ProfileEdit.vue +++ b/client/src/components/personalProfile/ProfileEdit.vue @@ -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"]); @@ -21,7 +23,12 @@ const formData = ref({ postal_code: user.postal_code, city: user.city, country_code: user.country?.country_code, + + phone_number: user.phone_number, + 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, @@ -31,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 = { ...profileData }; typedProfileData.country = countries.value.find( @@ -41,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"); } @@ -126,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" /> + + +
+ +
+ + +
+ +
+ @@ -264,6 +298,22 @@ async function avatarUpload(e: Event) { {{ $t("a.Firmenanschrift") }} +
+
+ + + +
+
+
diff --git a/client/src/components/personalProfile/ProfileView.vue b/client/src/components/personalProfile/ProfileView.vue index b5d2d06e..ac2e494c 100644 --- a/client/src/components/personalProfile/ProfileView.vue +++ b/client/src/components/personalProfile/ProfileView.vue @@ -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(() => { @@ -67,20 +71,45 @@ const invoiceAddress = computed(() => {

{{ $t("a.Persönliche Informationen") }}

-
{{ user.first_name }}
+
+ {{ user.first_name }} +
-
{{ user.last_name }}
+
+ {{ user.last_name }} +
-
{{ user.email }}
+
{{ user.email }}
+ +
+ + {{ displaySwissPhoneNumber(user.phone_number) }} + + {{ $t("a.Keine Angabe") }} +
+ +
+ + {{ dayjs(user.birth_date).format("DD.MM.YYYY") }} + + {{ $t("a.Keine Angabe") }} +
-
- +
+
+ + {{ line }} +
+
+
{{ $t("a.Keine Angabe") }}
@@ -89,14 +118,19 @@ const invoiceAddress = computed(() => {

{{ $t("a.Geschäftsdaten") }}

-
{{ organisationName }}
+
+ {{ organisationName }} +
- +
+ + {{ line }} +
+
+
{{ $t("a.Keine Angabe") }}
diff --git a/client/src/components/ui/ItDatePicker.vue b/client/src/components/ui/ItDatePicker.vue new file mode 100644 index 00000000..4c44f46d --- /dev/null +++ b/client/src/components/ui/ItDatePicker.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/client/src/components/ui/ItPersonRow.vue b/client/src/components/ui/ItPersonRow.vue index 98f2792b..c7383606 100644 --- a/client/src/components/ui/ItPersonRow.vue +++ b/client/src/components/ui/ItPersonRow.vue @@ -1,18 +1,30 @@