Add rewrite
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
<template>
|
||||
<NuxtRouteAnnouncer />
|
||||
<UApp :toaster="{ position: 'bottom-center' }">
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</UApp>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
vertical?: boolean;
|
||||
}
|
||||
const props = defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-between items-center gap-4">
|
||||
<div
|
||||
v-if="props.vertical"
|
||||
class="flex justify-between items-start gap-2 flex-col"
|
||||
>
|
||||
<div>
|
||||
<div class="font-semibold"><slot name="title" /></div>
|
||||
<div class="text-slate-600 text-sm hidden md:block">
|
||||
<slot name="description" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-nowrap w-full">
|
||||
<slot name="value" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="flex justify-between items-center gap-4">
|
||||
<div>
|
||||
<div class="font-semibold"><slot name="title" /></div>
|
||||
<div class="text-slate-600 text-sm hidden md:block">
|
||||
|
||||
122
app/components/rewrite-card.vue
Normal file
122
app/components/rewrite-card.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormSubmitEvent } from "@nuxt/ui";
|
||||
|
||||
const { user } = useUserSession();
|
||||
const toast = useToast();
|
||||
|
||||
const form = useTemplateRef("form");
|
||||
const submitting = ref(false);
|
||||
|
||||
const formData = {
|
||||
activityUrl: "",
|
||||
};
|
||||
|
||||
const validate = ({
|
||||
activityUrl,
|
||||
}: Partial<{
|
||||
activityUrl: string;
|
||||
}>) => {
|
||||
if (
|
||||
[
|
||||
"https://strava.com/activities/",
|
||||
"https://www.strava.com/activities/",
|
||||
].some((u) => activityUrl!.includes(u))
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
name: "activityUrl",
|
||||
message: "Please write a legit Strava activity URL.",
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const submit = async (event: FormSubmitEvent<typeof formData>) => {
|
||||
await $fetch("/api/rewrite", {
|
||||
method: "POST",
|
||||
query: {
|
||||
activity: event.data.activityUrl,
|
||||
},
|
||||
})
|
||||
.then(() =>
|
||||
toast.add({
|
||||
title: "Success",
|
||||
description: "Activity has been rewritten.",
|
||||
color: "success",
|
||||
}),
|
||||
)
|
||||
.catch(() =>
|
||||
toast.add({
|
||||
title: "Error",
|
||||
description: "Something wrong has happened. Maybe try again later.",
|
||||
color: "error",
|
||||
}),
|
||||
)
|
||||
.finally(() => {
|
||||
formData.activityUrl = "";
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm
|
||||
:disabled="!user.premium"
|
||||
ref="form"
|
||||
@submit="submit"
|
||||
:validate="validate"
|
||||
:state="formData"
|
||||
class="flex flex-col gap-8"
|
||||
>
|
||||
<template v-slot:default="errors">
|
||||
<UContainer class="max-w-2xl py-8 flex flex-col gap-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="font-bold text-lg">🔄 Re-write activity</div>
|
||||
<UTooltip
|
||||
arrow
|
||||
:disabled="user.premium"
|
||||
text="This feature is enabled for premium users. You can upgrade to
|
||||
premium by supporting Ghostwriter."
|
||||
>
|
||||
<UBadge variant="soft">Premium only</UBadge>
|
||||
</UTooltip>
|
||||
</div>
|
||||
<UCard>
|
||||
<div class="grid gap-4">
|
||||
<CardField vertical>
|
||||
<template #title> Activity URL </template>
|
||||
<template #description>
|
||||
Paste your Strava activity URL to rewrite it.
|
||||
</template>
|
||||
<template #value>
|
||||
<div class="grid gap-2">
|
||||
<UInput
|
||||
:disabled="submitting"
|
||||
v-model="formData.activityUrl"
|
||||
class="w-full"
|
||||
placeholder="https://www.strava.com/activities/12345678901"
|
||||
/>
|
||||
<div class="text-error-500 text-sm">
|
||||
{{ errors?.errors[0]?.message }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</CardField>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<UButton
|
||||
:disabled="!user.premium"
|
||||
loading-auto
|
||||
label="Rewrite"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
@click="form!.submit()"
|
||||
/>
|
||||
</template>
|
||||
</UCard>
|
||||
</UContainer>
|
||||
</template>
|
||||
</UForm>
|
||||
</template>
|
||||
@@ -3,41 +3,39 @@ const error = useError();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp>
|
||||
<UContainer class="max-w-lg p-8 md:p-16">
|
||||
<UCard>
|
||||
<div class="grid gap-4">
|
||||
<div class="text-center font-bold text-4xl md:text-6xl">
|
||||
{{ error?.statusCode }}
|
||||
</div>
|
||||
<NuxtImg
|
||||
class="rounded"
|
||||
src="https://plus.unsplash.com/premium_photo-1738036169480-086c0030803c?q=80&w=512&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
/>
|
||||
<UContainer class="max-w-lg p-8 md:p-16">
|
||||
<UCard>
|
||||
<div class="grid gap-4">
|
||||
<div class="text-center font-bold text-4xl md:text-6xl">
|
||||
{{ error?.statusCode }}
|
||||
</div>
|
||||
<NuxtImg
|
||||
class="rounded"
|
||||
src="https://plus.unsplash.com/premium_photo-1738036169480-086c0030803c?q=80&w=512&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
/>
|
||||
|
||||
<div class="text-center text-lg">
|
||||
Uh oh... Something went super wrong.
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="text-center font-mono p-4 bg-neutral-100 text-sm dark:bg-neutral-800 max-h-32 overflow-y-auto wrap-normal relative"
|
||||
>
|
||||
{{ error?.message }}
|
||||
</div>
|
||||
<div class="text-center text-lg">
|
||||
Uh oh... Something went super wrong.
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="w-full flex justify-center">
|
||||
<UButton
|
||||
label="Navigate back home"
|
||||
to="/"
|
||||
icon="heroicons:arrow-left"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</UContainer>
|
||||
<div
|
||||
class="text-center font-mono p-4 bg-neutral-100 text-sm dark:bg-neutral-800 max-h-32 overflow-y-auto wrap-normal relative"
|
||||
>
|
||||
{{ error?.message }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AppFooter />
|
||||
</UApp>
|
||||
<template #footer>
|
||||
<div class="w-full flex justify-center">
|
||||
<UButton
|
||||
label="Navigate back home"
|
||||
to="/"
|
||||
icon="heroicons:arrow-left"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</UContainer>
|
||||
|
||||
<AppFooter />
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<UApp>
|
||||
<UApp :toaster="{ position: 'bottom-center' }">
|
||||
<AuthState v-slot="{ loggedIn }">
|
||||
<Register v-if="!loggedIn" />
|
||||
<template v-if="loggedIn">
|
||||
|
||||
@@ -110,6 +110,8 @@ const saveOp = watchPausable(
|
||||
</UCard>
|
||||
</UContainer>
|
||||
|
||||
<RewriteCard />
|
||||
|
||||
<UContainer class="max-w-2xl py-8 flex flex-col gap-4">
|
||||
<div class="font-bold text-lg">🪪 Your connected Strava account</div>
|
||||
<UCard class="bg-neutral-50 dark:bg-slate-800">
|
||||
|
||||
Reference in New Issue
Block a user