setShowDeclineModal(true) : () => setShowDeleteModal(true)}
- onEditPress={() => navigate(`/edit-appointment/${appointment.id}`)}
- canceled={(appointment.declinedBy?.includes(user?.userID ?? '') ?? false) || canceled}
- isOver={isPastAppointment}
- isLast={isLastAppointment}
- />
+
+ {user?.student && (
+ <>
+
+
+ >
+ )}
+ {user?.pupil && (
+
+ )}
+
>
);
diff --git a/src/components/appointment/AppointmentMetaDetails.tsx b/src/components/appointment/AppointmentMetaDetails.tsx
index ef8bc0082..2e970d7e9 100644
--- a/src/components/appointment/AppointmentMetaDetails.tsx
+++ b/src/components/appointment/AppointmentMetaDetails.tsx
@@ -123,21 +123,24 @@ const AppointmentMetaDetails: React.FC = ({
)}
- {appointmentId && appointmentType && (
- <>
-
- >
- )}
+
+ {appointmentId && appointmentType && (
+ <>
+
+ >
+ )}
+
>
);
};
diff --git a/src/components/appointment/Buttons.tsx b/src/components/appointment/Buttons.tsx
deleted file mode 100644
index 09571583d..000000000
--- a/src/components/appointment/Buttons.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import { Stack, useBreakpointValue } from 'native-base';
-import { useTranslation } from 'react-i18next';
-import useApollo from '../../hooks/useApollo';
-import { useLayoutHelper } from '../../hooks/useLayoutHelper';
-import DisableableButton from '../DisablebleButton';
-
-type AvatarsProps = {
- onPress: () => void;
- onEditPress: () => void;
- canceled: boolean;
- isOver: boolean;
- isLast: boolean;
-};
-const Buttons: React.FC = ({ onPress, onEditPress, canceled, isOver, isLast }) => {
- const { isMobile } = useLayoutHelper();
- const { t } = useTranslation();
- const { user } = useApollo();
-
- const buttonWidth = useBreakpointValue({
- base: 'full',
- lg: '300',
- });
-
- return (
- <>
-
- {user?.student && (
- <>
-
- {t('appointment.detail.deleteButton')}
-
-
- {t('appointment.detail.editButton')}
-
- >
- )}
- {user?.pupil && (
-
- {t('appointment.detail.cancelButton')}
-
- )}
-
- >
- );
-};
-
-export default Buttons;
diff --git a/src/lang/ar.json b/src/lang/ar.json
index 245faaabb..5d4105fd5 100644
--- a/src/lang/ar.json
+++ b/src/lang/ar.json
@@ -33,7 +33,8 @@
},
"appointmentTile": {
"lecture": "الدرس #{{position}}",
- "title": ": {{appointmentTitle}}"
+ "title": ": {{appointmentTitle}}",
+ "cancelledBy": "تم إلغاؤه بواسطة {{name}}"
},
"create": {
"videoSelectOptions": {
@@ -101,7 +102,10 @@
"editButton": "تعديل الموعد",
"canceledToast": "تم إلغاء الموعد",
"zoomTooltipStudent": "إذا قمت بالنقر على هذا الرابط، ستنضم إلى الاجتماع كمشارك. للانضمام إلى الاجتماع كمضيف، استخدم زر \"الانضمام إلى دردشة الفيديو الآن\".",
- "zoomTooltipPupil": "لا تشارك هذا الرابط مع أشخاص آخرين. هذه هي الطريقة الوحيدة التي يمكننا من خلالها ضمان أمن المنصة."
+ "zoomTooltipPupil": "لا تشارك هذا الرابط مع أشخاص آخرين. هذه هي الطريقة الوحيدة التي يمكننا من خلالها ضمان أمن المنصة.",
+ "cancelledBy": "موعد {{name}} ألغيت هذه",
+ "rescheduleButton": "موعد المناوبة",
+ "rescheduleDescription": "موعد إذا قمت بتحريك، سيتم تحميل {{name}}} مرة أخرى."
},
"zoomModal": {
"useZoomApp": {
diff --git a/src/lang/de.json b/src/lang/de.json
index 28f6d8a20..81a02a425 100644
--- a/src/lang/de.json
+++ b/src/lang/de.json
@@ -59,7 +59,8 @@
},
"appointmentTile": {
"lecture": "Lektion #{{position}}",
- "title": ": {{appointmentTitle}}"
+ "title": ": {{appointmentTitle}}",
+ "cancelledBy": "Abgesagt von {{name}}"
},
"create": {
"assignmentHeader": "Für welches Lernangebot soll dieser Termin erstellt werden?",
@@ -112,6 +113,7 @@
"deleteButton": "Termin löschen",
"cancelButton": "Termin absagen",
"editButton": "Termin bearbeiten",
+ "rescheduleButton": "Termin verschieben",
"canceledToast": "Termin wurde abgesagt",
"zoomTooltipStudent": "Wenn du diesen Link aufrufst, trittst du dem Meeting als Teilnehmer:in bei. Um als Host dem Meeting beizutrteten nutze den Button \"Jetzt Videochat beitreten\".",
"zoomTooltipPupil": "Teile diesen Link niemals mit anderen Personen. Nur so können wir die Sicherheit der Plattform gewährleisten.",
@@ -127,7 +129,9 @@
"isOver": "Du kannst diesen Termin nicht absagen, da er bereits vorbei ist.",
"isCancelled": "Du hast diesen Termin bereits abgesagt."
}
- }
+ },
+ "cancelledBy": "{{name}} hat diesen Termin abgesagt",
+ "rescheduleDescription": "Wenn du den Termin verschiebst, wird {{name}} wieder eingeladen."
},
"zoomModal": {
"header": "Hinweise zum Videochat",
diff --git a/src/lang/en.json b/src/lang/en.json
index 8d3535353..b99141831 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -33,7 +33,8 @@
},
"appointmentTile": {
"lecture": "Lesson #{{position}}",
- "title": ": {{appointmentTitle}}"
+ "title": ": {{appointmentTitle}}",
+ "cancelledBy": "Canceled by {{name}}"
},
"create": {
"videoSelectOptions": {
@@ -101,7 +102,10 @@
"editButton": "Edit lecture",
"canceledToast": "Lecture was canceled",
"zoomTooltipStudent": "If you click on this link, you will join the meeting as a participant. To join the meeting as a host, use the \"Join video chat now\" button.",
- "zoomTooltipPupil": "Never share this link with other people. This is the only way we can guarantee the security of the platform."
+ "zoomTooltipPupil": "Never share this link with other people. This is the only way we can guarantee the security of the platform.",
+ "cancelledBy": "{{name}} has canceled this lecture ",
+ "rescheduleButton": "Reschedule appointment",
+ "rescheduleDescription": "If you reschedule the lecture, {{name}} will be invited again."
},
"zoomModal": {
"useZoomApp": {
diff --git a/src/lang/ru.json b/src/lang/ru.json
index 21ecc1591..760394417 100644
--- a/src/lang/ru.json
+++ b/src/lang/ru.json
@@ -33,7 +33,8 @@
},
"appointmentTile": {
"lecture": "Урок #{{position}}",
- "title": ": {{appointmentTitle}}"
+ "title": ": {{appointmentTitle}}",
+ "cancelledBy": "Отменено {{name}}"
},
"create": {
"videoSelectOptions": {
@@ -101,7 +102,10 @@
"editButton": "Редактировать назначение",
"canceledToast": "Назначение было отменено",
"zoomTooltipStudent": "участник Если вы нажмете на эту ссылку, вы присоединитесь к встрече в качестве . Чтобы присоединиться к встрече в качестве ведущего, воспользуйтесь кнопкой \"Присоединиться к видеочату сейчас\".",
- "zoomTooltipPupil": "Никогда не передавайте эту ссылку другим людям. Только так мы можем гарантировать безопасность платформы."
+ "zoomTooltipPupil": "Никогда не передавайте эту ссылку другим людям. Только так мы можем гарантировать безопасность платформы.",
+ "cancelledBy": "{{name}} отменил эту встречу",
+ "rescheduleButton": "Отложить встречу",
+ "rescheduleDescription": "Если вы отложите встречу, {{name}} будет приглашен снова."
},
"zoomModal": {
"useZoomApp": {
diff --git a/src/lang/tr.json b/src/lang/tr.json
index 64bf82403..fb23ca3c0 100644
--- a/src/lang/tr.json
+++ b/src/lang/tr.json
@@ -33,7 +33,8 @@
},
"appointmentTile": {
"lecture": "Ders #{{position}}",
- "title": ": {{appointmentTitle}}"
+ "title": ": {{appointmentTitle}}",
+ "cancelledBy": "{{name}} tarafından iptal edildi"
},
"create": {
"videoSelectOptions": {
@@ -101,7 +102,10 @@
"editButton": "Randevu düzenleme",
"canceledToast": "Randevu iptal edildi",
"zoomTooltipStudent": "Bu bağlantıya tıklarsanız, toplantıya katılımcı olarak katılırsınız. Toplantıya ev sahibi olarak katılmak için \"Video sohbete şimdi katıl\" düğmesini kullanın.",
- "zoomTooltipPupil": "Bu bağlantıyı asla başkalarıyla paylaşmayın. Platformun güvenliğini ancak bu şekilde garanti edebiliriz."
+ "zoomTooltipPupil": "Bu bağlantıyı asla başkalarıyla paylaşmayın. Platformun güvenliğini ancak bu şekilde garanti edebiliriz.",
+ "cancelledBy": "{{name}} bu randevuyu iptal etti",
+ "rescheduleButton": "Randevuyu ertele",
+ "rescheduleDescription": "Randevuyu ertelerseniz, {{name}} tekrar davet edilecektir."
},
"zoomModal": {
"useZoomApp": {
diff --git a/src/lang/uk.json b/src/lang/uk.json
index b91b4ab4b..eb621cb5e 100644
--- a/src/lang/uk.json
+++ b/src/lang/uk.json
@@ -33,7 +33,8 @@
},
"appointmentTile": {
"lecture": "Урок #{{position}}",
- "title": ": {{appointmentTitle}}"
+ "title": ": {{appointmentTitle}}",
+ "cancelledBy": "Скасовано {{name}}"
},
"create": {
"videoSelectOptions": {
@@ -101,7 +102,10 @@
"editButton": "Редагувати зустріч",
"canceledToast": "Зустріч було скасовано",
"zoomTooltipStudent": "Якщо ви натиснете на це посилання, ви приєднаєтеся до зустрічі як учасник. Щоб приєднатися до зустрічі як організатор, скористайтеся кнопкою \"Приєднатися до відеочату зараз\".",
- "zoomTooltipPupil": "Ніколи не діліться цим посиланням з іншими людьми. Це єдиний спосіб, яким ми можемо гарантувати безпеку платформи."
+ "zoomTooltipPupil": "Ніколи не діліться цим посиланням з іншими людьми. Це єдиний спосіб, яким ми можемо гарантувати безпеку платформи.",
+ "cancelledBy": "{{name}} скасував цю зустріч",
+ "rescheduleButton": "Перенести дату",
+ "rescheduleDescription": "Якщо ви відкладете зустріч, {{name}} буде запрошено знову."
},
"zoomModal": {
"useZoomApp": {
diff --git a/src/pages/SingleMatch.tsx b/src/pages/SingleMatch.tsx
index 40c9e936b..6aa78d1bb 100644
--- a/src/pages/SingleMatch.tsx
+++ b/src/pages/SingleMatch.tsx
@@ -80,12 +80,14 @@ query SingleMatchAppointments_NO_CACHE($matchId: Int!, $take: Float!, $skip: Flo
isOrganizer
isParticipant
override_meeting_link
+ declinedBy
organizers(skip: 0, take: 5) {
id
firstname
lastname
}
participants(skip: 0, take: 10) {
+ userID
id
firstname
lastname
diff --git a/src/widgets/AppointmentDay.tsx b/src/widgets/AppointmentDay.tsx
index 35027491e..6af3bf3a4 100644
--- a/src/widgets/AppointmentDay.tsx
+++ b/src/widgets/AppointmentDay.tsx
@@ -24,6 +24,7 @@ type Props = {
displayName: Appointment['displayName'];
appointmentId: Appointment['id'];
canJoinVideochat?: boolean;
+ declinedBy: Appointment['declinedBy'];
};
export const canJoinMeeting = (start: string, duration: number, joinBeforeMinutes: number, now: DateTime): boolean => {
@@ -49,6 +50,7 @@ const AppointmentDay: React.FC = ({
displayName,
appointmentId,
canJoinVideochat,
+ declinedBy,
}) => {
const isCurrentMonth = useCallback((start: string): boolean => {
const now = DateTime.now();
@@ -81,6 +83,8 @@ const AppointmentDay: React.FC = ({
lg: '100%',
});
+ const wasRejected = !!participants?.every((e) => declinedBy?.includes(e.userID!));
+
return (
<>
{!isReadOnly && organizers && participants ? (
@@ -103,6 +107,8 @@ const AppointmentDay: React.FC = ({
isOrganizer={isOrganizer}
displayName={displayName}
appointmentId={appointmentId}
+ wasRejected={wasRejected}
+ declinedBy={declinedBy}
/>
@@ -124,6 +130,8 @@ const AppointmentDay: React.FC = ({
displayName={displayName}
isReadOnly={isReadOnly}
appointmentId={appointmentId}
+ wasRejected={wasRejected}
+ declinedBy={declinedBy}
/>
diff --git a/src/widgets/AppointmentList.tsx b/src/widgets/AppointmentList.tsx
index a028340d3..608bdc5e0 100644
--- a/src/widgets/AppointmentList.tsx
+++ b/src/widgets/AppointmentList.tsx
@@ -158,6 +158,7 @@ const AppointmentList: React.FC = ({
isOrganizer={appointment.isOrganizer}
displayName={appointment.displayName}
appointmentId={appointment.id}
+ declinedBy={appointment.declinedBy}
/>
diff --git a/src/widgets/AppointmentTile.tsx b/src/widgets/AppointmentTile.tsx
index a7515bacf..648ec8201 100644
--- a/src/widgets/AppointmentTile.tsx
+++ b/src/widgets/AppointmentTile.tsx
@@ -1,5 +1,4 @@
import { Box, Card, HStack, VStack, Text, Avatar, Heading, useBreakpointValue, Spacer } from 'native-base';
-import WarningIcon from '../assets/icons/lernfair/icon_achtung.svg';
import StudentAvatar from '../assets/icons/lernfair/avatar_student.svg';
import PupilAvatar from '../assets/icons/lernfair/avatar_pupil.svg';
import { Pressable } from 'react-native';
@@ -7,6 +6,10 @@ import { AppointmentParticipant, Organizer } from '../gql/graphql';
import { useTranslation } from 'react-i18next';
import { Appointment } from '../types/lernfair/Appointment';
import VideoButton from '../components/VideoButton';
+import { IconInfoCircle, IconPointFilled } from '@tabler/icons-react';
+import { Typography } from '@/components/Typography';
+import { cn } from '@/lib/Tailwind';
+import { useUser } from '@/hooks/useApollo';
type Props = {
timeDescriptionText: string;
@@ -24,6 +27,8 @@ type Props = {
displayName: Appointment['displayName'];
appointmentId?: Appointment['id'];
canJoinVideochat?: boolean;
+ wasRejected: boolean;
+ declinedBy: Appointment['declinedBy'];
};
const AppointmentTile: React.FC = ({
@@ -40,32 +45,43 @@ const AppointmentTile: React.FC = ({
appointmentId,
appointmentType,
isOrganizer,
+ wasRejected,
+ declinedBy,
}) => {
const { t } = useTranslation();
const width = useBreakpointValue({
base: '100%',
lg: isFullWidth ? '92%' : '90%',
});
+ const { userID } = useUser();
const buttonWidth = useBreakpointValue({
base: 'full',
lg: '300',
});
+
+ const byMatch = !declinedBy?.includes(userID);
+ const isHighlighted = !isReadOnly && isCurrentlyTakingPlace && !wasRejected;
+ const wasRejectedByMatch = appointmentType === 'match' && wasRejected && byMatch;
+
return (
-
+
- {!isReadOnly && isCurrentlyTakingPlace && (
+ {isHighlighted && (
-
+
+
+
+
)}
-
+
{timeDescriptionText}
-
+
{organizers && participants && (
@@ -87,18 +103,26 @@ const AppointmentTile: React.FC = ({
)}
-
+
{displayName}
{position && (
-
+
{t('appointment.appointmentTile.lecture', { position: position }) +
(title ? t('appointment.appointmentTile.title', { appointmentTitle: title }) : '')}
)}
+ {wasRejectedByMatch && (
+
+
+
+ {t('appointment.appointmentTile.cancelledBy', { name: displayName })}
+
+
+ )}
- {!isReadOnly && isCurrentlyTakingPlace && appointmentId && appointmentType && (
+ {isHighlighted && appointmentId && appointmentType && (
Date: Mon, 14 Oct 2024 11:40:31 +0200
Subject: [PATCH 3/4] feat: Allow clickable emails on cooperation card (#678)
## What was done?
- Added utility to render emails as anchors in cooperation card
---
src/Utility.ts | 6 ++++++
src/pages/registration/PersonalData.tsx | 10 +++++++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/Utility.ts b/src/Utility.ts
index 611b96aaf..44ebe5125 100644
--- a/src/Utility.ts
+++ b/src/Utility.ts
@@ -155,6 +155,11 @@ export const sortByDate = {
+ const emailRegex = /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g;
+ return text.replace(emailRegex, '$1');
+};
+
const Utility = {
createToken,
toTimerString,
@@ -165,5 +170,6 @@ const Utility = {
handleDateString,
getTrafficStatus,
sortByDate,
+ renderTextWithEmailLinks,
};
export default Utility;
diff --git a/src/pages/registration/PersonalData.tsx b/src/pages/registration/PersonalData.tsx
index 470c43d10..36276ceb1 100644
--- a/src/pages/registration/PersonalData.tsx
+++ b/src/pages/registration/PersonalData.tsx
@@ -11,6 +11,8 @@ import isEmail from 'validator/es/lib/isEmail';
import { Cooperation } from '../../gql/graphql';
import { InfoCard } from '../../components/InfoCard';
import { usePageTitle } from '../../hooks/usePageTitle';
+import { Typography } from '@/components/Typography';
+import { renderTextWithEmailLinks } from '@/Utility';
export default function PersonalData({ cooperation }: { cooperation?: Cooperation }) {
const {
@@ -74,7 +76,13 @@ export default function PersonalData({ cooperation }: { cooperation?: Cooperatio
return (
- {cooperation && }
+ {cooperation && (
+
+
+
+
+
+ )}
From 867cb6d88bc0612b6c3b7ed0489a2ced4197897b Mon Sep 17 00:00:00 2001
From: John Angel <161815068+JeangelLF@users.noreply.github.com>
Date: Mon, 14 Oct 2024 12:22:53 +0200
Subject: [PATCH 4/4] Feat: Add back greeting on dashboard page (#679)
## Ticket
Partially tackles
https://github.com/corona-school/project-user/issues/1302
## What was done?
- Added back the greeting message but this time only on the Dashboard
page
---
src/pages/pupil/Dashboard.tsx | 6 ++++++
src/pages/pupil/ProfilePupil.tsx | 27 --------------------------
src/pages/student/DashboardStudent.tsx | 6 ++++++
src/pages/student/ProfileStudent.tsx | 16 ---------------
4 files changed, 12 insertions(+), 43 deletions(-)
diff --git a/src/pages/pupil/Dashboard.tsx b/src/pages/pupil/Dashboard.tsx
index 80d5e8a18..de10f2c8c 100644
--- a/src/pages/pupil/Dashboard.tsx
+++ b/src/pages/pupil/Dashboard.tsx
@@ -22,6 +22,7 @@ import { Lecture } from '../../gql/graphql';
import DisableableButton from '../../components/DisablebleButton';
import { useRoles } from '../../hooks/useApollo';
import ConfirmationModal from '@/modals/ConfirmationModal';
+import { Typography } from '@/components/Typography';
type Props = {};
@@ -220,6 +221,11 @@ const Dashboard: React.FC = () => {
{!called || (loading && )}
{called && !loading && (
+
+
+ {t('hallo')} {data?.me.firstname} 👋
+
+
diff --git a/src/pages/pupil/ProfilePupil.tsx b/src/pages/pupil/ProfilePupil.tsx
index f31cf5698..b5b591344 100644
--- a/src/pages/pupil/ProfilePupil.tsx
+++ b/src/pages/pupil/ProfilePupil.tsx
@@ -223,33 +223,6 @@ const ProfilePupil: React.FC = () => {
hideMenu={isMobileSM}
previousFallbackRoute="/settings"
headerTitle={t('profile.title')}
- headerContent={
-
-
-
-
- {data?.me?.firstname}
-
-
-
- }
headerLeft={
!isMobileSM && (
diff --git a/src/pages/student/DashboardStudent.tsx b/src/pages/student/DashboardStudent.tsx
index d90f62bcd..8028f12b3 100644
--- a/src/pages/student/DashboardStudent.tsx
+++ b/src/pages/student/DashboardStudent.tsx
@@ -26,6 +26,7 @@ import NextAppointmentCard from '../../widgets/NextAppointmentCard';
import { Lecture } from '../../gql/graphql';
import useApollo from '../../hooks/useApollo';
import { useUserType } from '../../hooks/useApollo';
+import { Typography } from '@/components/Typography';
type Props = {};
@@ -228,6 +229,11 @@ const DashboardStudent: React.FC = () => {
{!called || (loading && )}
{called && !loading && (
+
+
+ {t('hallo')} {data?.me.firstname} 👋
+
+
diff --git a/src/pages/student/ProfileStudent.tsx b/src/pages/student/ProfileStudent.tsx
index 29d70bb4b..806ff5d67 100644
--- a/src/pages/student/ProfileStudent.tsx
+++ b/src/pages/student/ProfileStudent.tsx
@@ -168,22 +168,6 @@ const ProfileStudent: React.FC = () => {
isLoading={loading}
previousFallbackRoute="/settings"
headerTitle={t('profile.title')}
- headerContent={
-
-
- {data?.me?.firstname}
-
-
- }
headerLeft={
!isMobileSM && (