From 05d7619cc1864faf8948da6ef9113c4c92d524ab Mon Sep 17 00:00:00 2001
From: Croos3r <dorian.moy@epitech.eu>
Date: Thu, 18 Jul 2024 11:19:35 +0200
Subject: [PATCH] feat(redirection-warning): added LLink component to proxy
 every link by a warning if it goes outside of the current site

---
 src/components/LLink.vue                      | 44 +++++++++++++++++++
 .../ui/alert-dialog/AlertDialog.vue           | 14 ++++++
 .../ui/alert-dialog/AlertDialogAction.vue     | 20 +++++++++
 .../ui/alert-dialog/AlertDialogCancel.vue     | 22 ++++++++++
 .../ui/alert-dialog/AlertDialogContent.vue    | 42 ++++++++++++++++++
 .../alert-dialog/AlertDialogDescription.vue   | 25 +++++++++++
 .../ui/alert-dialog/AlertDialogFooter.vue     | 21 +++++++++
 .../ui/alert-dialog/AlertDialogHeader.vue     | 16 +++++++
 .../ui/alert-dialog/AlertDialogTitle.vue      | 22 ++++++++++
 .../ui/alert-dialog/AlertDialogTrigger.vue    | 11 +++++
 src/components/ui/alert-dialog/index.ts       |  9 ++++
 src/components/ui/button/Button.vue           | 26 +++++++++++
 src/components/ui/button/index.ts             | 35 +++++++++++++++
 13 files changed, 307 insertions(+)
 create mode 100644 src/components/LLink.vue
 create mode 100644 src/components/ui/alert-dialog/AlertDialog.vue
 create mode 100644 src/components/ui/alert-dialog/AlertDialogAction.vue
 create mode 100644 src/components/ui/alert-dialog/AlertDialogCancel.vue
 create mode 100644 src/components/ui/alert-dialog/AlertDialogContent.vue
 create mode 100644 src/components/ui/alert-dialog/AlertDialogDescription.vue
 create mode 100644 src/components/ui/alert-dialog/AlertDialogFooter.vue
 create mode 100644 src/components/ui/alert-dialog/AlertDialogHeader.vue
 create mode 100644 src/components/ui/alert-dialog/AlertDialogTitle.vue
 create mode 100644 src/components/ui/alert-dialog/AlertDialogTrigger.vue
 create mode 100644 src/components/ui/alert-dialog/index.ts
 create mode 100644 src/components/ui/button/Button.vue
 create mode 100644 src/components/ui/button/index.ts

diff --git a/src/components/LLink.vue b/src/components/LLink.vue
new file mode 100644
index 0000000..460a41b
--- /dev/null
+++ b/src/components/LLink.vue
@@ -0,0 +1,44 @@
+<script lang="ts" setup>
+import { LinkHTMLAttributes, ref } from "vue";
+import { AlertDialog, AlertDialogContent, AlertDialogTrigger } from "@/components/ui/alert-dialog";
+import { AlertDialogOverlay, AlertDialogPortal } from "radix-vue";
+import LButton from "@/components/LButton.vue";
+
+interface IProps extends /* @vue-ignore */ LinkHTMLAttributes {}
+
+withDefaults(defineProps<IProps>(), {});
+
+const open = ref(false);
+</script>
+
+<template>
+	<RouterLink v-if="href && href.startsWith('/')" :to="href">
+		<slot />
+	</RouterLink>
+	<AlertDialog v-model:open="open">
+		<AlertDialogTrigger as-child>
+			<a>
+				<slot />
+			</a>
+		</AlertDialogTrigger>
+		<AlertDialogPortal>
+			<AlertDialogOverlay class="fixed inset-0 z-30 backdrop-blur-sm" />
+			<AlertDialogContent class="flex flex-col gap-y-6 bg-primary px-6 py-9 text-neutral-white">
+				<h6 class="body-default text-center font-bold">Leaving our website</h6>
+				<p class="body-tiny text-center">
+					You will be redirected to an external website <strong class="font-bold">({{ $attrs.href }})</strong> that is
+					not under our control. We cannot guarantee the accuracy, security, or reliability of the information provided
+					on these external sites.
+				</p>
+				<div class="gap-x- flex justify-center gap-x-3">
+					<a v-bind="$attrs">
+						<LButton class="w-[150px]" light text="Continue" @click="open = false" />
+					</a>
+					<LButton class="w-[150px]" light text="Cancel" @click="open = false" />
+				</div>
+			</AlertDialogContent>
+		</AlertDialogPortal>
+	</AlertDialog>
+</template>
+
+<style scoped></style>
diff --git a/src/components/ui/alert-dialog/AlertDialog.vue b/src/components/ui/alert-dialog/AlertDialog.vue
new file mode 100644
index 0000000..8fb30de
--- /dev/null
+++ b/src/components/ui/alert-dialog/AlertDialog.vue
@@ -0,0 +1,14 @@
+<script setup lang="ts">
+import { type AlertDialogEmits, type AlertDialogProps, AlertDialogRoot, useForwardPropsEmits } from 'radix-vue'
+
+const props = defineProps<AlertDialogProps>()
+const emits = defineEmits<AlertDialogEmits>()
+
+const forwarded = useForwardPropsEmits(props, emits)
+</script>
+
+<template>
+  <AlertDialogRoot v-bind="forwarded">
+    <slot />
+  </AlertDialogRoot>
+</template>
diff --git a/src/components/ui/alert-dialog/AlertDialogAction.vue b/src/components/ui/alert-dialog/AlertDialogAction.vue
new file mode 100644
index 0000000..092d6cd
--- /dev/null
+++ b/src/components/ui/alert-dialog/AlertDialogAction.vue
@@ -0,0 +1,20 @@
+<script setup lang="ts">
+import { type HTMLAttributes, computed } from 'vue'
+import { AlertDialogAction, type AlertDialogActionProps } from 'radix-vue'
+import { cn } from '@/lib/utils'
+import { buttonVariants } from '@/components/ui/button'
+
+const props = defineProps<AlertDialogActionProps & { class?: HTMLAttributes['class'] }>()
+
+const delegatedProps = computed(() => {
+  const { class: _, ...delegated } = props
+
+  return delegated
+})
+</script>
+
+<template>
+  <AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)">
+    <slot />
+  </AlertDialogAction>
+</template>
diff --git a/src/components/ui/alert-dialog/AlertDialogCancel.vue b/src/components/ui/alert-dialog/AlertDialogCancel.vue
new file mode 100644
index 0000000..907f24c
--- /dev/null
+++ b/src/components/ui/alert-dialog/AlertDialogCancel.vue
@@ -0,0 +1,22 @@
+<script lang="ts" setup>
+import { computed, type HTMLAttributes } from "vue";
+import { AlertDialogCancel, type AlertDialogCancelProps } from "radix-vue";
+import { cn } from "@/lib/utils";
+import { buttonVariants } from "@/components/ui/button";
+
+const props = defineProps<AlertDialogCancelProps & { class?: HTMLAttributes["class"] }>();
+
+const delegatedProps = computed(() => {
+	const { class: _, ...delegated } = props;
+	return delegated;
+});
+</script>
+
+<template>
+	<AlertDialogCancel
+		:class="cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', props.class)"
+		v-bind="delegatedProps"
+	>
+		<slot />
+	</AlertDialogCancel>
+</template>
diff --git a/src/components/ui/alert-dialog/AlertDialogContent.vue b/src/components/ui/alert-dialog/AlertDialogContent.vue
new file mode 100644
index 0000000..93ef36f
--- /dev/null
+++ b/src/components/ui/alert-dialog/AlertDialogContent.vue
@@ -0,0 +1,42 @@
+<script lang="ts" setup>
+import { computed, type HTMLAttributes } from "vue";
+import {
+	AlertDialogContent,
+	type AlertDialogContentEmits,
+	type AlertDialogContentProps,
+	AlertDialogOverlay,
+	AlertDialogPortal,
+	useForwardPropsEmits,
+} from "radix-vue";
+import { cn } from "@/lib/utils";
+
+const props = defineProps<AlertDialogContentProps & { class?: HTMLAttributes["class"] }>();
+const emits = defineEmits<AlertDialogContentEmits>();
+
+const delegatedProps = computed(() => {
+	const { class: _, ...delegated } = props;
+
+	return delegated;
+});
+
+const forwarded = useForwardPropsEmits(delegatedProps, emits);
+</script>
+
+<template>
+	<AlertDialogPortal>
+		<AlertDialogOverlay
+			class="bg-black/80 fixed inset-0 z-50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
+		/>
+		<AlertDialogContent
+			:class="
+				cn(
+					'fixed left-1/2 top-1/2 z-50 grid w-[45vw] max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
+					props.class,
+				)
+			"
+			v-bind="forwarded"
+		>
+			<slot />
+		</AlertDialogContent>
+	</AlertDialogPortal>
+</template>
diff --git a/src/components/ui/alert-dialog/AlertDialogDescription.vue b/src/components/ui/alert-dialog/AlertDialogDescription.vue
new file mode 100644
index 0000000..9682cbb
--- /dev/null
+++ b/src/components/ui/alert-dialog/AlertDialogDescription.vue
@@ -0,0 +1,25 @@
+<script setup lang="ts">
+import { type HTMLAttributes, computed } from 'vue'
+import {
+  AlertDialogDescription,
+  type AlertDialogDescriptionProps,
+} from 'radix-vue'
+import { cn } from '@/lib/utils'
+
+const props = defineProps<AlertDialogDescriptionProps & { class?: HTMLAttributes['class'] }>()
+
+const delegatedProps = computed(() => {
+  const { class: _, ...delegated } = props
+
+  return delegated
+})
+</script>
+
+<template>
+  <AlertDialogDescription
+    v-bind="delegatedProps"
+    :class="cn('text-sm text-muted-foreground', props.class)"
+  >
+    <slot />
+  </AlertDialogDescription>
+</template>
diff --git a/src/components/ui/alert-dialog/AlertDialogFooter.vue b/src/components/ui/alert-dialog/AlertDialogFooter.vue
new file mode 100644
index 0000000..55d0a0e
--- /dev/null
+++ b/src/components/ui/alert-dialog/AlertDialogFooter.vue
@@ -0,0 +1,21 @@
+<script setup lang="ts">
+import type { HTMLAttributes } from 'vue'
+import { cn } from '@/lib/utils'
+
+const props = defineProps<{
+  class?: HTMLAttributes['class']
+}>()
+</script>
+
+<template>
+  <div
+    :class="
+      cn(
+        'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
+        props.class,
+      )
+    "
+  >
+    <slot />
+  </div>
+</template>
diff --git a/src/components/ui/alert-dialog/AlertDialogHeader.vue b/src/components/ui/alert-dialog/AlertDialogHeader.vue
new file mode 100644
index 0000000..c61c449
--- /dev/null
+++ b/src/components/ui/alert-dialog/AlertDialogHeader.vue
@@ -0,0 +1,16 @@
+<script setup lang="ts">
+import type { HTMLAttributes } from 'vue'
+import { cn } from '@/lib/utils'
+
+const props = defineProps<{
+  class?: HTMLAttributes['class']
+}>()
+</script>
+
+<template>
+  <div
+    :class="cn('flex flex-col gap-y-2 text-center sm:text-left', props.class)"
+  >
+    <slot />
+  </div>
+</template>
diff --git a/src/components/ui/alert-dialog/AlertDialogTitle.vue b/src/components/ui/alert-dialog/AlertDialogTitle.vue
new file mode 100644
index 0000000..50c583d
--- /dev/null
+++ b/src/components/ui/alert-dialog/AlertDialogTitle.vue
@@ -0,0 +1,22 @@
+<script setup lang="ts">
+import { type HTMLAttributes, computed } from 'vue'
+import { AlertDialogTitle, type AlertDialogTitleProps } from 'radix-vue'
+import { cn } from '@/lib/utils'
+
+const props = defineProps<AlertDialogTitleProps & { class?: HTMLAttributes['class'] }>()
+
+const delegatedProps = computed(() => {
+  const { class: _, ...delegated } = props
+
+  return delegated
+})
+</script>
+
+<template>
+  <AlertDialogTitle
+    v-bind="delegatedProps"
+    :class="cn('text-lg font-semibold', props.class)"
+  >
+    <slot />
+  </AlertDialogTitle>
+</template>
diff --git a/src/components/ui/alert-dialog/AlertDialogTrigger.vue b/src/components/ui/alert-dialog/AlertDialogTrigger.vue
new file mode 100644
index 0000000..4f5e2fd
--- /dev/null
+++ b/src/components/ui/alert-dialog/AlertDialogTrigger.vue
@@ -0,0 +1,11 @@
+<script setup lang="ts">
+import { AlertDialogTrigger, type AlertDialogTriggerProps } from 'radix-vue'
+
+const props = defineProps<AlertDialogTriggerProps>()
+</script>
+
+<template>
+  <AlertDialogTrigger v-bind="props">
+    <slot />
+  </AlertDialogTrigger>
+</template>
diff --git a/src/components/ui/alert-dialog/index.ts b/src/components/ui/alert-dialog/index.ts
new file mode 100644
index 0000000..91d138a
--- /dev/null
+++ b/src/components/ui/alert-dialog/index.ts
@@ -0,0 +1,9 @@
+export { default as AlertDialog } from './AlertDialog.vue'
+export { default as AlertDialogTrigger } from './AlertDialogTrigger.vue'
+export { default as AlertDialogContent } from './AlertDialogContent.vue'
+export { default as AlertDialogHeader } from './AlertDialogHeader.vue'
+export { default as AlertDialogTitle } from './AlertDialogTitle.vue'
+export { default as AlertDialogDescription } from './AlertDialogDescription.vue'
+export { default as AlertDialogFooter } from './AlertDialogFooter.vue'
+export { default as AlertDialogAction } from './AlertDialogAction.vue'
+export { default as AlertDialogCancel } from './AlertDialogCancel.vue'
diff --git a/src/components/ui/button/Button.vue b/src/components/ui/button/Button.vue
new file mode 100644
index 0000000..5cfd668
--- /dev/null
+++ b/src/components/ui/button/Button.vue
@@ -0,0 +1,26 @@
+<script setup lang="ts">
+import type { HTMLAttributes } from 'vue'
+import { Primitive, type PrimitiveProps } from 'radix-vue'
+import { type ButtonVariants, buttonVariants } from '.'
+import { cn } from '@/lib/utils'
+
+interface Props extends PrimitiveProps {
+  variant?: ButtonVariants['variant']
+  size?: ButtonVariants['size']
+  class?: HTMLAttributes['class']
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  as: 'button',
+})
+</script>
+
+<template>
+  <Primitive
+    :as="as"
+    :as-child="asChild"
+    :class="cn(buttonVariants({ variant, size }), props.class)"
+  >
+    <slot />
+  </Primitive>
+</template>
diff --git a/src/components/ui/button/index.ts b/src/components/ui/button/index.ts
new file mode 100644
index 0000000..1b00c32
--- /dev/null
+++ b/src/components/ui/button/index.ts
@@ -0,0 +1,35 @@
+import { type VariantProps, cva } from 'class-variance-authority'
+
+export { default as Button } from './Button.vue'
+
+export const buttonVariants = cva(
+  'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
+  {
+    variants: {
+      variant: {
+        default: 'bg-primary text-primary-foreground hover:bg-primary/90',
+        destructive:
+          'bg-destructive text-destructive-foreground hover:bg-destructive/90',
+        outline:
+          'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
+        secondary:
+          'bg-secondary text-secondary-foreground hover:bg-secondary/80',
+        ghost: 'hover:bg-accent hover:text-accent-foreground',
+        link: 'text-primary underline-offset-4 hover:underline',
+      },
+      size: {
+        default: 'h-10 px-4 py-2',
+        xs: 'h-7 rounded px-2',
+        sm: 'h-9 rounded-md px-3',
+        lg: 'h-11 rounded-md px-8',
+        icon: 'h-10 w-10',
+      },
+    },
+    defaultVariants: {
+      variant: 'default',
+      size: 'default',
+    },
+  },
+)
+
+export type ButtonVariants = VariantProps<typeof buttonVariants>