Skip to content

Commit 513908b

Browse files
committed
fixes(fe): added translation for pending request, added translation for matching swipe right request modal headline, added text to swiping to make it more clear and improve ux, delete data gives a (fake) toast that data is deleted
1 parent 373ee7d commit 513908b

9 files changed

Lines changed: 326 additions & 260 deletions

File tree

Frontend/changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- added translation for pending request
2+
- added translation for matching swipe right request modal headline
3+
- added text to swiping to make it more clear and improve ux
4+
- delete data gives a (fake) toast that data is deleted
Lines changed: 107 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,132 @@
11
<script setup lang="ts">
2-
import { usePointerSwipe } from '@vueuse/core'
3-
import { computed, shallowRef } from 'vue'
2+
import { usePointerSwipe } from "@vueuse/core";
3+
import { computed, shallowRef, ref } from "vue";
44
55
interface Props {
6-
swipeThreshold?: number
6+
swipeThreshold?: number;
7+
swipeLeftText?: string;
8+
swipeRightText?: string;
79
}
810
911
const props = withDefaults(defineProps<Props>(), {
10-
swipeThreshold: 40,
11-
})
12+
swipeThreshold: 40,
13+
swipeLeftText: "Request",
14+
swipeRightText: "Reject",
15+
});
1216
13-
const emit = defineEmits(['swipeRight', 'swipeLeft'])
17+
const emit = defineEmits(["swipeRight", "swipeLeft"]);
1418
1519
defineExpose({
16-
reset,
17-
})
20+
reset,
21+
});
1822
19-
const target = shallowRef<HTMLElement | null>(null)
20-
const container = shallowRef<HTMLElement | null>(null)
23+
const target = shallowRef<HTMLElement | null>(null);
24+
const container = shallowRef<HTMLElement | null>(null);
2125
22-
const containerWidth = computed(() => container.value?.offsetWidth || 0)
26+
const containerWidth = computed(() => container.value?.offsetWidth || 0);
2327
24-
const left = shallowRef('0')
25-
const opacity = shallowRef(1)
28+
const left = shallowRef("0");
29+
const opacity = shallowRef(1);
2630
27-
const cardIsAtStartPosition = ref(false)
31+
const cardIsAtStartPosition = ref(false);
2832
2933
function reset() {
30-
left.value = '0'
31-
opacity.value = 1
32-
cardIsAtStartPosition.value = true
34+
left.value = "0";
35+
opacity.value = 1;
36+
cardIsAtStartPosition.value = true;
3337
}
3438
3539
const { distanceX, isSwiping } = usePointerSwipe(target, {
36-
disableTextSelect: true,
37-
threshold: props.swipeThreshold,
38-
onSwipe() {
39-
cardIsAtStartPosition.value = false
40-
if (containerWidth.value) {
41-
if (distanceX.value < 0) {
42-
// Swipe right direction (negative distanceX)
43-
const distance = Math.abs(distanceX.value)
44-
left.value = `${distance}px`
45-
opacity.value = 1.25 - distance / containerWidth.value
46-
}
47-
else if (distanceX.value > 0) {
48-
// Swipe left direction (positive distanceX)
49-
const distance = distanceX.value
50-
left.value = `-${distance}px`
51-
opacity.value = 1.25 - distance / containerWidth.value
52-
}
53-
else {
54-
left.value = '0'
55-
opacity.value = 1
56-
}
57-
}
58-
},
59-
onSwipeEnd() {
60-
if (containerWidth.value) {
61-
// Calculate the ratio relative to container width
62-
const ratio = Math.abs(distanceX.value) / containerWidth.value
63-
64-
if (distanceX.value < 0 && ratio >= 0.5) {
65-
// Swipe right direction threshold reached
66-
left.value = '100%'
67-
opacity.value = 0
68-
emit('swipeRight')
69-
}
70-
else if (distanceX.value > 0 && ratio >= 0.5) {
71-
// Swipe left direction threshold reached
72-
left.value = '-100%'
73-
opacity.value = 0
74-
emit('swipeLeft')
75-
}
76-
else {
77-
// Reset if threshold not reached
78-
cardIsAtStartPosition.value = true
79-
left.value = '0'
80-
opacity.value = 1
81-
}
82-
}
83-
},
84-
})
40+
disableTextSelect: true,
41+
threshold: props.swipeThreshold,
42+
onSwipe() {
43+
cardIsAtStartPosition.value = false;
44+
if (containerWidth.value) {
45+
if (distanceX.value < 0) {
46+
// Swipe right direction (negative distanceX)
47+
const distance = Math.abs(distanceX.value);
48+
left.value = `${distance}px`;
49+
opacity.value = 1.25 - distance / containerWidth.value;
50+
} else if (distanceX.value > 0) {
51+
// Swipe left direction (positive distanceX)
52+
const distance = distanceX.value;
53+
left.value = `-${distance}px`;
54+
opacity.value = 1.25 - distance / containerWidth.value;
55+
} else {
56+
left.value = "0";
57+
opacity.value = 1;
58+
}
59+
}
60+
},
61+
onSwipeEnd() {
62+
if (containerWidth.value) {
63+
// Calculate the ratio relative to container width
64+
const ratio = Math.abs(distanceX.value) / containerWidth.value;
8565
86-
const backgroundColorClasses = computed(() => {
87-
return {
88-
'bg-base-100': cardIsAtStartPosition.value,
89-
'bg-success': distanceX.value < -props.swipeThreshold/10,
90-
'bg-error': distanceX.value > props.swipeThreshold/10,
66+
if (distanceX.value < 0 && ratio >= 0.5) {
67+
// Swipe right direction threshold reached
68+
left.value = "100%";
69+
opacity.value = 0;
70+
emit("swipeRight");
71+
} else if (distanceX.value > 0 && ratio >= 0.5) {
72+
// Swipe left direction threshold reached
73+
left.value = "-100%";
74+
opacity.value = 0;
75+
emit("swipeLeft");
76+
} else {
77+
// Reset if threshold not reached
78+
cardIsAtStartPosition.value = true;
79+
left.value = "0";
80+
opacity.value = 1;
81+
}
9182
}
92-
})
83+
},
84+
});
85+
86+
const backgroundColorClasses = computed(() => ({
87+
"bg-base-100": cardIsAtStartPosition.value,
88+
"bg-success": distanceX.value < -props.swipeThreshold / 10,
89+
"bg-error": distanceX.value > props.swipeThreshold / 10,
90+
}));
91+
92+
const swipeText = computed(() => {
93+
if (distanceX.value < -props.swipeThreshold / 10) return props.swipeLeftText;
94+
if (distanceX.value > props.swipeThreshold / 10) return props.swipeRightText;
95+
return "";
96+
});
9397
98+
const textPositionClass = computed(() => {
99+
if (distanceX.value < -props.swipeThreshold / 10) return "left-4";
100+
if (distanceX.value > props.swipeThreshold / 10) return "right-4";
101+
return "";
102+
});
94103
</script>
95104

96105
<template>
97-
<div class="inline-flex">
98-
<div
99-
ref="container"
100-
class="rounded-3xl relative inline-block overflow-hidden shadow-lg"
101-
:class="backgroundColorClasses"
102-
>
103-
<div
104-
ref="target"
105-
class="relative w-full flex items-center justify-center"
106-
:class="{ 'transition-all duration-200 ease-linear': !isSwiping }"
107-
:style="{ left, opacity }"
108-
>
109-
<slot />
110-
</div>
111-
</div>
106+
<div class="inline-flex">
107+
<div
108+
ref="container"
109+
class="rounded-3xl relative inline-block overflow-hidden shadow-lg"
110+
:class="backgroundColorClasses"
111+
>
112+
<div
113+
ref="target"
114+
class="relative w-full flex items-center justify-center"
115+
:class="{ 'transition-all duration-200 ease-linear': !isSwiping }"
116+
:style="{ left, opacity }"
117+
>
118+
<slot />
119+
</div>
120+
<!-- Overlay text for swipe direction -->
121+
<div
122+
v-if="swipeText"
123+
class="absolute top-1/2 transform -translate-y-1/2 pointer-events-none"
124+
:class="textPositionClass"
125+
>
126+
<span class="text-xl font-bold text-white">
127+
{{ swipeText }}
128+
</span>
129+
</div>
112130
</div>
113-
</template>
131+
</div>
132+
</template>

Frontend/i18n/locales/de-DE.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@
7878
"dataDeletionSubtitle": "🗑 Datenlöschung",
7979
"deleteMyData": "Meine Daten löschen",
8080
"generalInfoText": "Bei SuperWise nehmen wir Ihre Privatsphäre und den Schutz Ihrer persönlichen Daten sehr ernst. Diese Richtlinie beschreibt, wie wir Ihre Daten erfassen, verwenden und verwalten – für maximale Transparenz und Ihr beruhigtes Gewissen.",
81-
"generalInfoTitle": "🛡️ SuperWise Datenschutzrichtlinie"
81+
"generalInfoTitle": "🛡️ SuperWise Datenschutzrichtlinie",
82+
"dataDeleted": "Daten erfolgreich gelöscht"
8283
},
8384
"student&supervisor": {
8485
"changesAndOverwritesText": "Wenn Sie persönliche Daten wie Ihren Namen, Ihr Profilbild, die Themenbeschreibung oder die Biografie aktualisieren, wird die vorherige Version dauerhaft überschrieben. Wir speichern keine früheren Versionen oder Backups Ihrer persönlichen Daten.",
@@ -149,6 +150,9 @@
149150
"sentRequest": "Betreuungsanfrage wurde gesendet",
150151
"supervisionRequest": "{name} will von dir betreut werden!",
151152
"supervisorDismissed": "Betreuer wurde abgelehnt",
153+
"swipeRightHeadline": "{firstName} {lastName} anfragen",
154+
"swipeLeftText": "Anfragen",
155+
"swipeRightText": "Entfernen",
152156
"swipeLeft": {
153157
"description": "Wenn Sie {firstName} {lastName} entfernen, wird er nie wieder vorgeschlagen, Sie können jedoch weiterhin nach ihm suchen. Sind Sie sicher, dass Sie fortfahren möchten?",
154158
"headline": "Betreuer entfernen",
@@ -164,7 +168,7 @@
164168
"confirm": "Bestätigen",
165169
"dontShowAgain": "Nicht mehr anzeigen",
166170
"sendSupervisionRequestHeadline": "Betreuungsanfrage senden",
167-
"supervisionInfo": "Wenn du eine Anfrage zur Betreuung einer Abschlussarbeit sendest, wird diese an die ausgewählten Betreuenden weitergeleitet. Sie können dann entscheiden, ob sie die Anfrage annehmen oder ablehnen möchten. Wenn sie die Anfrage annehmen, wird eine Bestätigung erstellt, die du herunterladen kannst.",
171+
"supervisionInfo": "Wenn du eine Anfrage an {firstName} {lastName} sendest, wird er/sie benachrichtigt und kann die Anfrage annehmen oder ablehnen. Wenn er/sie die Anfrage annimmt, erhältst Du eine automatisch erstellte Bestätigung, die du herunterladen kannst. Wenn er/sie ablehnt, solltest du einen weiteren Betreuer anfragen.",
168172
"withdrawSupervisionConfirm": "Anfrage zurückziehen",
169173
"withdrawSupervisionDescription": "Möchten Sie die Betreuungsanfrage von {name} zurückziehen? Nach dem Zurückziehen kann der Betreuer diese Anfrage nicht mehr sehen.",
170174
"withdrawSupervisionHeadline": "Betreuungsanfrage zurückziehen"

Frontend/i18n/locales/en-GB.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@
7878
"dataDeletionSubtitle": "🗑 Data Deletion",
7979
"deleteMyData": "Delete my data",
8080
"generalInfoText": "At SuperWise, we take your privacy and the protection of your personal data seriously. This policy outlines how we collect, use, and manage your information to ensure transparency and your peace of mind.",
81-
"generalInfoTitle": "🛡 SuperWise Data Protection Policy"
81+
"generalInfoTitle": "🛡 SuperWise Data Protection Policy",
82+
"dataDeleted": "Data deleted successfully"
8283
},
8384
"student&supervisor": {
8485
"changesAndOverwritesText": "When you update your personal data, such as your name, profile picture, topic description, or bio, the previous version is permanently overwritten. We do not retain copies or backups of your previous personal data.",
@@ -149,6 +150,9 @@
149150
"sentRequest": "Supervision request has been sent",
150151
"supervisionRequest": "{name} wants to be supervised by you!",
151152
"supervisorDismissed": "Supervisor has been dismissed",
153+
"swipeRightHeadline": "Request {firstName} {lastName}",
154+
"swipeLeftText": "Request",
155+
"swipeRightText": "Reject",
152156
"swipeLeft": {
153157
"description": "By dismissing {firstName} {lastName}, they will never get suggested again, but you can still search for them. Are you sure you want to do this?",
154158
"headline": "Dismiss Supervisor",
@@ -164,7 +168,7 @@
164168
"confirm": "Confirm",
165169
"dontShowAgain": "Don't show again",
166170
"sendSupervisionRequestHeadline": "Send supervision request",
167-
"supervisionInfo": "When you send a thesis supervision request, it will be forwarded to the selected supervisors. They can then decide whether to accept or decline the request. If they accept the request, a confirmation will be created that you can download.",
171+
"supervisionInfo": "When you send a thesis supervision request to {firstName} {lastName}, they will be notified and can accept or deny your request. If they accept the request, a confirmation will be created that you can download, otherwise you will need to choose a different supervisor.",
168172
"withdrawSupervisionConfirm": "Withdraw Request",
169173
"withdrawSupervisionDescription": "Would you like to withdraw the supervision request from {name}? Once withdrawn, the supervisor will no longer see this request.",
170174
"withdrawSupervisionHeadline": "Withdraw Supervision Request"
Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<script lang="ts" setup>
2-
32
const pageContent = [
43
"dataProtection.generic.generalInfoTitle",
54
"dataProtection.generic.generalInfoText",
@@ -18,15 +17,25 @@ const pageContent = [
1817
];
1918
2019
const getStyle = (content: string) => {
21-
if (content.match(/Subtitle(\.\d+)?$/))
22-
return "pt-6";
20+
if (content.match(/Subtitle(\.\d+)?$/)) return "pt-6";
2321
};
2422
2523
const { t } = useI18n();
2624
25+
const toastData = ref({
26+
visible: false,
27+
type: "success",
28+
message: "",
29+
});
30+
2731
const deleteData = () => {
2832
// Implement the logic to delete data
2933
console.log("Delete data button clicked");
34+
toastData.value = {
35+
visible: true,
36+
type: "success",
37+
message: t("dataProtection.generic.dataDeleted"),
38+
};
3039
};
3140
3241
definePageMeta({
@@ -37,30 +46,41 @@ definePageMeta({
3746
<template>
3847
<div class="p-8 flex flex-col gap-2">
3948
<div
40-
v-for="content in pageContent"
41-
:key="content"
42-
:class="getStyle(content)"
49+
v-for="content in pageContent"
50+
:key="content"
51+
:class="getStyle(content)"
4352
>
4453
<ul v-if="content.match(/List(\.\d+)?$/)" class="list-disc pl-5">
4554
<li v-for="(item, index) in t(content).split('\n')" :key="index">
4655
{{ item }}
4756
</li>
4857
</ul>
4958

59+
<h2 v-else-if="content.match(/Subtitle(\.\d+)?$/)" class="text-large">
60+
{{ t(content) }}
61+
</h2>
5062

51-
<h2 v-else-if="content.match(/Subtitle(\.\d+)?$/)" class="text-large">{{ t(content) }}</h2>
52-
53-
<h1 v-else-if="content.match(/Title(\.\d+)?$/)" class="text-header">{{ t(content) }}</h1>
63+
<h1 v-else-if="content.match(/Title(\.\d+)?$/)" class="text-header">
64+
{{ t(content) }}
65+
</h1>
5466

5567
<p v-else class="text-body">{{ t(content) }}</p>
5668
</div>
5769
<CustomButton
58-
:text="t('dataProtection.generic.deleteMyData')"
59-
block
60-
class="py-8"
61-
color="error"
62-
left-icon="trash-can"
63-
@click="deleteData"
70+
:text="t('dataProtection.generic.deleteMyData')"
71+
block
72+
class="py-8"
73+
color="error"
74+
left-icon="trash-can"
75+
@click="deleteData"
76+
/>
77+
<Toast
78+
v-if="toastData.visible"
79+
:duration="3000"
80+
:message="toastData.message"
81+
:type="toastData.type"
82+
@close="toastData.visible = false"
83+
@button-click="toastData.visible = false"
6484
/>
6585
</div>
6686
</template>

0 commit comments

Comments
 (0)