Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Frontend/components/AdminHeader/AdminHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { useColorMode } from "#imports";
const router = useRouter();
const colorMode = useColorMode();

const { t } = useI18n();

interface Props {
variant?: "default" | "upload" | "download" | "delete" | "text" | "warning";
headerText: string;
Expand Down Expand Up @@ -60,7 +62,7 @@ const emit = defineEmits([ 'right-button-click' ]);
/>

<ClientOnly v-else>
<div class="navbar-center">
<div aria-hidden="true" class="navbar-center">
<img
:src="
colorMode.value === 'dark'
Expand All @@ -73,8 +75,11 @@ const emit = defineEmits([ 'right-button-click' ]);
</div>
</ClientOnly>

<!--For some reason the aria-label is not read out... so i have disabled it for now!-->
<span
:aria-label="t('aria.pageHeader', {pageName: props.headerText})"
:class="{colorText}"
aria-hidden="true"
class="text-header bg-info-soft w-full"
>
{{ props.headerText }}
Expand Down
5 changes: 5 additions & 0 deletions Frontend/components/AppHeader/AppHeader.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts" setup>
import { useRouter } from "vue-router";
import { useColorMode } from "#imports";
import { useI18n } from "vue-i18n";

const router = useRouter();
const colorMode = useColorMode();
Expand All @@ -23,6 +24,8 @@ const props = withDefaults(defineProps<AppHeaderProps>(), {
modelValue: "",
});

const { t } = useI18n();

const goBack = () => {
router.back();
};
Expand Down Expand Up @@ -58,6 +61,7 @@ const goBack = () => {
: '../images/appHeader_logo_light.svg'
"
alt="Logo image"
aria-hidden="true"
class="h-6"
>
</ClientOnly>
Expand All @@ -73,6 +77,7 @@ const goBack = () => {

<CustomButton
v-if="props.showSearch"
:aria-label="t('appHeader.search')"
class="w-10"
color="default"
right-icon="fa-magnifying-glass"
Expand Down
28 changes: 14 additions & 14 deletions Frontend/components/Avatar/Avatar.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts" setup>
import {computed, onMounted, ref, watchEffect} from 'vue';
import { computed, onMounted, ref, watchEffect } from 'vue';

/**
* Avatar component displays a user's profile image with various customization options.
Expand Down Expand Up @@ -214,7 +214,7 @@ const addPosition = computed(() => {

// Handle image loading errors
const handleImageError = () => {
console.log(`Image Error: ${props.src}`);
console.log(`Image Error: ${ props.src }`);
imgError.value = true;
};

Expand All @@ -224,28 +224,28 @@ const handleImageError = () => {
<div :class="onlineStatus" class="avatar">
<div :class="[imgSize, shapeClass, ringClass]">
<img
:key="imgKey"
:alt="props.alt"
:src="(props.src && !imgError)
:key="imgKey"
:alt="props.alt"
:src="(props.src && !imgError)
? props.src
: getPlaceholderImage(props.firstName, props.lastName)"
class="object-cover w-full h-full"
loading="lazy"
@error="handleImageError"
class="object-cover w-full h-full"
loading="lazy"
@error="handleImageError"
>
</div>
<button
v-if="props.emoji"
:class="[emojiPosition, emojiStyle]"
class="btn btn-soft absolute rounded-full ring-base-100 ring-offset-base-100 ring-1 ring-offset-1 flex items-center justify-center aspect-square p-0"
v-if="props.emoji"
:class="[emojiPosition, emojiStyle]"
class="btn btn-soft absolute rounded-full ring-base-100 ring-offset-base-100 ring-1 ring-offset-1 flex items-center justify-center aspect-square p-0"
>
{{ props.emoji }}
</button>

<button
v-if="props.addButton"
:class="[addPosition, buttonStyle]"
class="btn absolute rounded-full ring-base-100 ring-offset-base-100 ring-1 ring-offset-1 flex items-center justify-center aspect-square p-0"
v-if="props.addButton"
:class="[addPosition, buttonStyle]"
class="btn absolute rounded-full ring-base-100 ring-offset-base-100 ring-1 ring-offset-1 flex items-center justify-center aspect-square p-0"
>+
</button>

Expand Down
4 changes: 3 additions & 1 deletion Frontend/components/SupervisorCard/SupervisorCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const descriptionClasses = computed(() => ({

<template>
<div
:class="cardSizeClasses" class="card bg-base-100 shadow-lg border border-base-300">
:class="cardSizeClasses" aria-hidden="true" class="card bg-base-100 shadow-lg border border-base-300">
<div class="card-body">
<h2 class="card-title font-bold">
<Avatar
Expand All @@ -151,6 +151,8 @@ const descriptionClasses = computed(() => ({
:last-name="props.lastName"
:size="props.size"
:src="props.image"
aria-hidden="true"

/>
<p class="text-large capitalize">
{{ props.firstName }} {{ props.lastName }}
Expand Down
201 changes: 114 additions & 87 deletions Frontend/components/SwipeContainer/SwipeContainer.vue
Original file line number Diff line number Diff line change
@@ -1,113 +1,140 @@
<script setup lang="ts">
import { usePointerSwipe } from '@vueuse/core'
import { computed, shallowRef } from 'vue'
<script lang="ts" setup>
import { usePointerSwipe } from '@vueuse/core';
import { computed, ref, shallowRef } from 'vue';

interface Props {
swipeThreshold?: number
swipeThreshold?: number;
ariaLabel?: string;
name?: string;
}

const { t } = useI18n();

const props = withDefaults(defineProps<Props>(), {
swipeThreshold: 40,
})
swipeThreshold: 40,
ariaLabel: 'Unnamed Swipe Container',
name: 'Supervisor',
});

const emit = defineEmits(['swipeRight', 'swipeLeft'])
const emit = defineEmits([ 'swipeRight', 'swipeLeft' ]);

defineExpose({
reset,
})
reset,
});

const target = shallowRef<HTMLElement | null>(null)
const container = shallowRef<HTMLElement | null>(null)
const target = shallowRef<HTMLElement | null>(null);
const container = shallowRef<HTMLElement | null>(null);

const containerWidth = computed(() => container.value?.offsetWidth || 0)

const left = shallowRef('0')
const opacity = shallowRef(1)
const left = shallowRef('0');
const opacity = shallowRef(1);

const cardIsAtStartPosition = ref(false)
const cardIsAtStartPosition = ref(false);

function reset() {
left.value = '0'
opacity.value = 1
cardIsAtStartPosition.value = true
left.value = '0';
opacity.value = 1;
cardIsAtStartPosition.value = true;
}

const { distanceX, isSwiping } = usePointerSwipe(target, {
disableTextSelect: true,
threshold: props.swipeThreshold,
onSwipe() {
cardIsAtStartPosition.value = false
if (containerWidth.value) {
if (distanceX.value < 0) {
// Swipe right direction (negative distanceX)
const distance = Math.abs(distanceX.value)
left.value = `${distance}px`
opacity.value = 1.25 - distance / containerWidth.value
}
else if (distanceX.value > 0) {
// Swipe left direction (positive distanceX)
const distance = distanceX.value
left.value = `-${distance}px`
opacity.value = 1.25 - distance / containerWidth.value
}
else {
left.value = '0'
opacity.value = 1
}
}
},
onSwipeEnd() {
if (containerWidth.value) {
// Calculate the ratio relative to container width
const ratio = Math.abs(distanceX.value) / containerWidth.value

if (distanceX.value < 0 && ratio >= 0.5) {
// Swipe right direction threshold reached
left.value = '100%'
opacity.value = 0
emit('swipeRight')
}
else if (distanceX.value > 0 && ratio >= 0.5) {
// Swipe left direction threshold reached
left.value = '-100%'
opacity.value = 0
emit('swipeLeft')
}
else {
// Reset if threshold not reached
cardIsAtStartPosition.value = true
left.value = '0'
opacity.value = 1
}
}
},
})
disableTextSelect: true,
threshold: props.swipeThreshold,
onSwipe() {
cardIsAtStartPosition.value = false
if (containerWidth.value) {
if (distanceX.value < 0) {
// Swipe right direction (negative distanceX)
const distance = Math.abs(distanceX.value)
left.value = `${ distance }px`
opacity.value = 1.25 - distance / containerWidth.value
} else if (distanceX.value > 0) {
// Swipe left direction (positive distanceX)
const distance = distanceX.value
left.value = `-${ distance }px`
opacity.value = 1.25 - distance / containerWidth.value
} else {
left.value = '0'
opacity.value = 1
}
}
},
onSwipeEnd() {
if (containerWidth.value) {
// Calculate the ratio relative to container width
const ratio = Math.abs(distanceX.value) / containerWidth.value

const backgroundColorClasses = computed(() => {
return {
'bg-base-100': cardIsAtStartPosition.value,
'bg-success': distanceX.value < -props.swipeThreshold/10,
'bg-error': distanceX.value > props.swipeThreshold/10,
if (distanceX.value < 0 && ratio >= 0.5) {
// Swipe right direction threshold reached
left.value = '100%'
opacity.value = 0
emit('swipeRight')
} else if (distanceX.value > 0 && ratio >= 0.5) {
// Swipe left direction threshold reached
left.value = '-100%'
opacity.value = 0
emit('swipeLeft')
} else {
// Reset if threshold not reached
cardIsAtStartPosition.value = true
left.value = '0'
opacity.value = 1
}
}
},
})

const backgroundColorClasses = computed(() => {
return {
'bg-base-100': cardIsAtStartPosition.value,
'bg-success': distanceX.value < -props.swipeThreshold / 10,
'bg-error': distanceX.value > props.swipeThreshold / 10,
}
});

</script>

<template>
<div class="inline-flex">
<div
ref="container"
class="rounded-3xl relative inline-block overflow-hidden shadow-lg"
:class="backgroundColorClasses"
>
<div
ref="target"
class="relative w-full flex items-center justify-center"
:class="{ 'transition-all duration-200 ease-linear': !isSwiping }"
:style="{ left, opacity }"
>
<slot />
</div>
</div>
<div :aria-label="props.ariaLabel" class="inline-flex">
<div
ref="container"
:class="backgroundColorClasses"
class="rounded-3xl relative inline-block overflow-hidden shadow-lg"
>
<div
ref="target"
:class="{
'transition-all duration-200 ease-linear':
!isSwiping,
}"
:style="{ left, opacity }"
class="relative w-full flex items-center justify-center"
>
<slot/>
</div>
<!-- Screen reader only action buttons -->

<button
:aria-label="
t('matching.aria.matchWith', {
name: props.name,
}) + '.' +props.ariaLabel
"
class="sr-only sr-only-focusable"
type="button"
@click="emit('swipeRight')"
/>
<button
:aria-label="
t('matching.aria.reject', {
name: props.name,
})
"
class="sr-only sr-only-focusable"
type="button"
@click="emit('swipeLeft')"
/>
</div>
</template>
</div>
</template>
Loading