mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 15:58:38 +01:00
new selfhosted version
This commit is contained in:
184
dashboard/pages/shared/[linkid].vue
Normal file
184
dashboard/pages/shared/[linkid].vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<script lang="ts" setup>
|
||||
import LineDataNew from '~/components/complex/LineDataNew.vue';
|
||||
import { CalendarIcon, MoonIcon, SunIcon, Flame } from 'lucide-vue-next'
|
||||
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||
import ActionableChart from '~/components/complex/ActionableChart.vue';
|
||||
import { toast } from 'vue-sonner';
|
||||
|
||||
const route = useRoute();
|
||||
const sharedLinkId = route.params.linkid;
|
||||
const { sharedLink, timeValue, sharedPassword, needPassword } = useShared();
|
||||
const df = new DateFormatter('en-US', { dateStyle: 'medium' })
|
||||
const popoverOpen = ref<boolean>(false);
|
||||
|
||||
|
||||
const { data: live_users, status: live_users_status, refresh: live_users_refresh } = useAuthFetch(`/api/share/live_users?linkId=${sharedLinkId}`);
|
||||
|
||||
let interval: any;
|
||||
|
||||
onMounted(async () => {
|
||||
const info = await useAuthFetchSync(`/api/share/info?linkId=${sharedLinkId}`);
|
||||
needPassword.value = info.hasPassword;
|
||||
sharedLink.value = sharedLinkId.toString();
|
||||
interval = setInterval(() => {
|
||||
live_users_refresh();
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (interval) clearInterval(interval)
|
||||
})
|
||||
|
||||
|
||||
function setDays(days: number) {
|
||||
const start = new Date(Date.now() - 1000 * 60 * 60 * 24 * days);
|
||||
const end = new Date();
|
||||
timeValue.value.start = new CalendarDate(start.getUTCFullYear(), start.getUTCMonth() + 1, start.getUTCDate());
|
||||
timeValue.value.end = new CalendarDate(end.getUTCFullYear(), end.getUTCMonth() + 1, end.getUTCDate());
|
||||
}
|
||||
|
||||
const currentPassword = ref<string>("");
|
||||
|
||||
async function reloadData() {
|
||||
const ok = await useAuthFetchSync(`/api/share/verify?linkId=${sharedLinkId}&password=${currentPassword.value}`);
|
||||
if (ok === true) {
|
||||
sharedPassword.value = currentPassword.value;
|
||||
} else {
|
||||
currentPassword.value = '';
|
||||
toast('Error', { description: 'Password wrong', position: 'top-right' });
|
||||
}
|
||||
}
|
||||
|
||||
const showDashboard = computed(() => {
|
||||
if (!sharedLink.value) return false;
|
||||
if (needPassword.value && sharedPassword.value) return true;
|
||||
if (!needPassword.value) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
const colorMode = useColorMode()
|
||||
const isDark = computed({
|
||||
get() {
|
||||
return colorMode.value === 'dark'
|
||||
},
|
||||
set() {
|
||||
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<div class="h-dvh w-full overflow-hidden flex flex-col poppins bg-gray-100 dark:bg-background">
|
||||
<div v-if="showDashboard" class="h-dvh w-full overflow-hidden flex flex-col poppins">
|
||||
<div class="w-full flex justify-between pl-4 pr-6 py-4 bg-sidebar border-solid border-b-1">
|
||||
<div class="flex gap-4">
|
||||
<Popover v-model:open="popoverOpen">
|
||||
<PopoverTrigger as-child>
|
||||
<Button variant="outline">
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
<template v-if="timeValue.start">
|
||||
<template v-if="timeValue.end">
|
||||
{{ df.format(timeValue.start.toDate(getLocalTimeZone())) }} - {{
|
||||
df.format(timeValue.end.toDate(getLocalTimeZone())) }}
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
{{ df.format(timeValue.start.toDate(getLocalTimeZone())) }}
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
Pick a date
|
||||
</template>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-4 flex flex-col items-end relative z-[90]">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<Button @click="setDays(1)"> Today </Button>
|
||||
<Button @click="setDays(7)"> Last week </Button>
|
||||
<Button @click="setDays(30)"> Last 30 days </Button>
|
||||
<Button @click="setDays(60)"> Last 60 days </Button>
|
||||
<Button @click="setDays(90)"> Last 90 days </Button>
|
||||
</div>
|
||||
<RangeCalendar v-model="timeValue" initial-focus :number-of-months="1"
|
||||
@update:start-value="(startDate) => timeValue.start = startDate" />
|
||||
</div>
|
||||
<Button @click="popoverOpen = false;"> Confirm </Button>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<div class="flex items-center gap-1 poppins bg-border/20 py-1 px-2 rounded-md">
|
||||
<div class="size-2 bg-green-500 rounded-full animate-pulse mr-1"></div>
|
||||
<div v-if="live_users != undefined && live_users_status === 'success'">{{ live_users }}</div>
|
||||
<Loader v-else class="!size-4"></Loader>
|
||||
<div> live </div>
|
||||
</div>
|
||||
</div>
|
||||
<Button @click="isDark = !isDark" variant="outline">
|
||||
<SunIcon v-if="isDark"></SunIcon>
|
||||
<MoonIcon v-else />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="p-4 flex flex-col gap-4 grow poppins overflow-y-auto h-full">
|
||||
<Accordion type="single" collapsible class="relative lg:hidden border rounded-xl px-5 bg-card">
|
||||
<AccordionItem value=" top-cards">
|
||||
<AccordionTrigger class="text-md">
|
||||
Top Charts
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<DashboardTopCards class="grid grid-cols-2" />
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
<div class="hidden lg:block">
|
||||
<DashboardTopCards />
|
||||
</div>
|
||||
|
||||
|
||||
<ActionableChart></ActionableChart>
|
||||
|
||||
|
||||
<div class="flex w-full justify-center">
|
||||
<div class="flex w-full gap-4 flex-col xl:flex-row">
|
||||
<LineDataNew :shared-link="sharedLink.toString()" class="flex-1" type="referrers" select />
|
||||
<LineDataNew :shared-link="sharedLink.toString()" class="flex-1" type="pages" select />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full justify-center">
|
||||
<div class="flex w-full gap-4 flex-col xl:flex-row">
|
||||
<LineDataNew :shared-link="sharedLink.toString()" class="flex-1" type="countries" select />
|
||||
<LineDataNew :shared-link="sharedLink.toString()" class="flex-1" type="devices" select />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="bg-[url('/planet.png')] bg-center bg-cover rounded-xl min-h-40 lg:min-h-60 flex items-center justify-evenly px-6 lg:px-12">
|
||||
<div class="flex flex-col gap-2 ">
|
||||
<span class="text-lg lg:text-5xl font-semibold text-white">
|
||||
Want these metrics for your website?
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<NuxtLink to="https://dashboard.litlyx.com/register" target="_blank">
|
||||
<Button size="lg" class="bg-white text-black hover:bg-gray-200 transition p-8 text-lg">
|
||||
Try for free
|
||||
</Button>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="p-8 flex items-center justify-center h-full flex-col gap-4">
|
||||
<label>Enter Password</label>
|
||||
<CustomPasswordInput class="w-[60dvw] lg:w-[32dvw]" v-model="currentPassword"></CustomPasswordInput>
|
||||
<Button class="w-[60dvw] lg:w-[32dvw]" size="lg" @click="reloadData()"> Enter </Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user