From 092d18bbe4e4ef504e463d9a91e7b762c6004049 Mon Sep 17 00:00:00 2001 From: TZGyn Date: Sat, 25 Nov 2023 11:43:54 +0800 Subject: [PATCH] added visitor by country stat and visitor by city --- frontend/bun.lockb | Bin 145998 -> 149871 bytes .../drizzle/0002_robust_the_executioner.sql | 1 + frontend/drizzle/meta/0002_snapshot.json | 187 ++++++++++++++++++ frontend/drizzle/meta/_journal.json | 45 +++-- frontend/src/lib/components/ui/tabs/index.ts | 18 ++ .../components/ui/tabs/tabs-content.svelte | 21 ++ .../lib/components/ui/tabs/tabs-list.svelte | 19 ++ .../components/ui/tabs/tabs-trigger.svelte | 23 +++ frontend/src/lib/db/schema.ts | 1 + .../routes/(app)/links/[id]/+page.server.ts | 31 ++- .../src/routes/(app)/links/[id]/+page.svelte | 53 ++++- redirect/src/index.ts | 1 + redirect/src/types.ts | 1 + 13 files changed, 374 insertions(+), 27 deletions(-) create mode 100644 frontend/drizzle/0002_robust_the_executioner.sql create mode 100644 frontend/drizzle/meta/0002_snapshot.json create mode 100644 frontend/src/lib/components/ui/tabs/index.ts create mode 100644 frontend/src/lib/components/ui/tabs/tabs-content.svelte create mode 100644 frontend/src/lib/components/ui/tabs/tabs-list.svelte create mode 100644 frontend/src/lib/components/ui/tabs/tabs-trigger.svelte diff --git a/frontend/bun.lockb b/frontend/bun.lockb index ac4a2ac5440ae6280feafa23a0e5e82ffe75d868..58618398af223a3ff1b6bb38114a1357a99b13f7 100755 GIT binary patch delta 24586 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%0A>i#nx94#TRQ_p0WmCa}7nGMIY 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

3pkVUes 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 diff --git a/frontend/drizzle/0002_robust_the_executioner.sql b/frontend/drizzle/0002_robust_the_executioner.sql new file mode 100644 index 0000000..dedfe46 --- /dev/null +++ b/frontend/drizzle/0002_robust_the_executioner.sql @@ -0,0 +1 @@ +ALTER TABLE "visitor" ADD COLUMN "city" varchar(255) NOT NULL; \ No newline at end of file diff --git a/frontend/drizzle/meta/0002_snapshot.json b/frontend/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..8addc8d --- /dev/null +++ b/frontend/drizzle/meta/0002_snapshot.json @@ -0,0 +1,187 @@ +{ + "id": "8f8ae49c-43bf-4e07-a30d-383e2b767524", + "prevId": "548a0489-b083-464f-800c-4dee6f1ff161", + "version": "5", + "dialect": "pg", + "tables": { + "session": { + "name": "session", + "schema": "", + "columns": { + "token": { + "name": "token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "shortener": { + "name": "shortener", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "link": { + "name": "link", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": false, + "notNull": false, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + } + }, + "visitor": { + "name": "visitor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "shortener_id": { + "name": "shortener_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "country_code": { + "name": "country_code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "city": { + "name": "city", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/frontend/drizzle/meta/_journal.json b/frontend/drizzle/meta/_journal.json index 7f6eb6d..638942c 100644 --- a/frontend/drizzle/meta/_journal.json +++ b/frontend/drizzle/meta/_journal.json @@ -1,20 +1,27 @@ { - "version": "5", - "dialect": "pg", - "entries": [ - { - "idx": 0, - "version": "5", - "when": 1699851315914, - "tag": "0000_nebulous_energizer", - "breakpoints": true - }, - { - "idx": 1, - "version": "5", - "when": 1700134783172, - "tag": "0001_regular_microchip", - "breakpoints": true - } - ] -} + "version": "5", + "dialect": "pg", + "entries": [ + { + "idx": 0, + "version": "5", + "when": 1699851315914, + "tag": "0000_nebulous_energizer", + "breakpoints": true + }, + { + "idx": 1, + "version": "5", + "when": 1700134783172, + "tag": "0001_regular_microchip", + "breakpoints": true + }, + { + "idx": 2, + "version": "5", + "when": 1700882455122, + "tag": "0002_robust_the_executioner", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/frontend/src/lib/components/ui/tabs/index.ts b/frontend/src/lib/components/ui/tabs/index.ts new file mode 100644 index 0000000..968804c --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/index.ts @@ -0,0 +1,18 @@ +import { Tabs as TabsPrimitive } from "bits-ui"; +import Content from "./tabs-content.svelte"; +import List from "./tabs-list.svelte"; +import Trigger from "./tabs-trigger.svelte"; + +const Root = TabsPrimitive.Root; + +export { + Root, + Content, + List, + Trigger, + // + Root as Tabs, + Content as TabsContent, + List as TabsList, + Trigger as TabsTrigger +}; diff --git a/frontend/src/lib/components/ui/tabs/tabs-content.svelte b/frontend/src/lib/components/ui/tabs/tabs-content.svelte new file mode 100644 index 0000000..3866292 --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/tabs-content.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/frontend/src/lib/components/ui/tabs/tabs-list.svelte b/frontend/src/lib/components/ui/tabs/tabs-list.svelte new file mode 100644 index 0000000..8905c77 --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/tabs-list.svelte @@ -0,0 +1,19 @@ + + + + + diff --git a/frontend/src/lib/components/ui/tabs/tabs-trigger.svelte b/frontend/src/lib/components/ui/tabs/tabs-trigger.svelte new file mode 100644 index 0000000..d8cac33 --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/tabs-trigger.svelte @@ -0,0 +1,23 @@ + + + + + diff --git a/frontend/src/lib/db/schema.ts b/frontend/src/lib/db/schema.ts index b32a040..f3c4832 100644 --- a/frontend/src/lib/db/schema.ts +++ b/frontend/src/lib/db/schema.ts @@ -51,6 +51,7 @@ export const visitor = pgTable('visitor', { length: 255, }).notNull(), country: varchar('country', { length: 255 }).notNull(), + city: varchar('city', { length: 255 }).notNull(), }) export const visitorRelations = relations(visitor, ({ one }) => ({ diff --git a/frontend/src/routes/(app)/links/[id]/+page.server.ts b/frontend/src/routes/(app)/links/[id]/+page.server.ts index 8d878d9..6be7404 100644 --- a/frontend/src/routes/(app)/links/[id]/+page.server.ts +++ b/frontend/src/routes/(app)/links/[id]/+page.server.ts @@ -19,6 +19,10 @@ export const load = (async (event) => { }, }) + if (!shortener) { + throw redirect(303, '/') + } + const visitor = await db .select({ count: sql`cast(count(*) as int)`, @@ -27,9 +31,28 @@ export const load = (async (event) => { .from(visitorSchema) .groupBy(sql`to_char(${visitorSchema.createdAt}, 'MM')`) - if (!shortener) { - throw redirect(303, '/') - } + const visitorByCountry = await db + .select({ + count: sql`cast(count(*) as int)`, + country: visitorSchema.country, + code: visitorSchema.countryCode, + }) + .from(visitorSchema) + .groupBy(visitorSchema.country, visitorSchema.countryCode) + + const visitorByCity = await db + .select({ + count: sql`cast(count(*) as int)`, + country: visitorSchema.country, + code: visitorSchema.countryCode, + city: visitorSchema.city, + }) + .from(visitorSchema) + .groupBy( + visitorSchema.country, + visitorSchema.countryCode, + visitorSchema.city, + ) - return { shortener, visitor } + return { shortener, visitor, visitorByCountry, visitorByCity } }) satisfies PageServerLoad diff --git a/frontend/src/routes/(app)/links/[id]/+page.svelte b/frontend/src/routes/(app)/links/[id]/+page.svelte index 03e3b4d..acbb0b0 100644 --- a/frontend/src/routes/(app)/links/[id]/+page.svelte +++ b/frontend/src/routes/(app)/links/[id]/+page.svelte @@ -2,6 +2,7 @@ import type { PageData } from './$types' import { Separator } from '$lib/components/ui/separator' import * as Card from '$lib/components/ui/card' + import * as Tabs from '$lib/components/ui/tabs' import type { ApexOptions } from 'apexcharts' import { mode } from 'mode-watcher' import { onMount } from 'svelte' @@ -84,15 +85,15 @@ if (container) { container.innerHTML = '' } - var chart = new apexChart(container, options) + var chart = new ApexChart(container, options) chart.render() } - $: container && apexChart && renderChart(options) + $: container && ApexChart && renderChart(options) - let apexChart: typeof ApexCharts + let ApexChart: typeof ApexCharts onMount(async () => { - apexChart = (await import('apexcharts')).default + ApexChart = (await import('apexcharts')).default }) @@ -112,4 +113,48 @@

+ + + +
+ Visitors + Visitors by Country/City +
+ + Country + City + +
+ + + {#each data.visitorByCountry as visitorByCountry} +
+
+ +
{visitorByCountry.country}
+
+
{visitorByCountry.count}
+
+ {/each} +
+ + {#each data.visitorByCity as visitorByCity} +
+
+ +
{visitorByCity.city}
+
+
{visitorByCity.count}
+
+ {/each} +
+
+
+
diff --git a/redirect/src/index.ts b/redirect/src/index.ts index b586425..31cc042 100644 --- a/redirect/src/index.ts +++ b/redirect/src/index.ts @@ -31,6 +31,7 @@ app.get( country: geolocation.data.location.country.name as string, country_code: geolocation.data.location.country .alpha2 as string, + city: geolocation.data.location.city.name as string, } await db.insertInto('visitor').values(visitor_data).execute() diff --git a/redirect/src/types.ts b/redirect/src/types.ts index a405199..ba2e849 100644 --- a/redirect/src/types.ts +++ b/redirect/src/types.ts @@ -30,6 +30,7 @@ export interface VisitorTable { shortener_id: number country: string country_code: string + city: string created_at: ColumnType }