Skip to content

Commit

Permalink
Merge pull request #11 from fayazara/feat-fayaz-changes
Browse files Browse the repository at this point in the history
feat: Project overview
  • Loading branch information
fayazara authored Aug 10, 2023
2 parents dd71e33 + 36691dd commit 6d731a8
Show file tree
Hide file tree
Showing 9 changed files with 277 additions and 166 deletions.
9 changes: 5 additions & 4 deletions components/Dashboard/Feedback/Details.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,22 @@
icon="i-heroicons-ellipsis-vertical"
class="-my-1"
size="xs"
@click="isOpen = false"
/>
<UButton
color="gray"
variant="soft"
icon="i-heroicons-x-mark-20-solid"
class="-my-1"
size="xs"
@click="isOpen = false"
@click="$emit('toggleFeedbackDetails')"
/>
</div>
</template>

<Placeholder class="h-full" />
<!-- <Placeholder class="h-full" /> -->
</UCard>
</template>

<script lang="ts" setup></script>
<script lang="ts" setup>
defineEmits(["toggleFeedbackDetails"]);
</script>
73 changes: 73 additions & 0 deletions components/Dashboard/Projects/Header.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<template>
<header>
<div
class="col-span-full border-b dark:border-white/10 flex flex-col items-start justify-between gap-x-8 gap-y-4 bg-gray-100 dark:bg-gray-700/10 px-4 py-4 sm:flex-row sm:items-center sm:px-6 lg:px-8"
>
<div>
<div class="flex items-center gap-x-3">
<UAvatar :src="project.avatar" size="sm" class="ring-white/10 ring-2"/>
<h1 class="flex gap-x-3 text-base leading-7">
<span class="font-semibold">{{ project.name }}</span>
</h1>
</div>
<p class="mt-1 text-xs leading-6 text-gray-500">
{{ project.description }}
</p>
</div>
<UButton
label="Add"
icon="i-heroicons-plus-circle"
color="white"
></UButton>
</div>
<div
class="grid grid-cols-2 lg:grid-cols-6 gap-px bg-gray-200 dark:bg-white/10"
>
<div
v-for="stat in stats"
:key="stat.name"
class="py-4 sm:py-6 px-4 sm:px-6 lg:px-8 bg-gray-50 dark:bg-gray-950"
>
<p
class="text-sm font-medium leading-6 text-gray-600 dark:text-gray-400"
>
{{ stat.name }}
</p>
<p class="mt-1 sm:mt-2 flex items-baseline gap-x-2">
<span
class="text-2xl lg:text-4xl font-semibold tracking-tight text-gray-700 dark:text-white/70"
>{{ stat.value }}</span
>
<span
v-if="stat.unit"
class="text-sm text-gray-600 dark:text-gray-400"
>{{ stat.unit }}</span
>
</p>
</div>
</div>
</header>
</template>

<script setup>
const props = defineProps({
project: {
type: Object,
required: true,
},
stats: {
type: Object,
required: true,
},
});
const stats = computed(() => {
return [
{ name: "Total feedbacks", value: props.stats?.feedbackCount },
{ name: "Ideas", value: props.stats?.idea || 0 },
{ name: "Issues", value: props.stats?.issue || 0 },
{ name: "Other", value: props.stats?.other || 0 },
{ name: "Open", value: props.stats?.open || 0 },
{ name: "Closed", value: props.stats?.closed || 0 },
];
});
</script>
13 changes: 13 additions & 0 deletions lib/enums/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,16 @@ export enum Status {
active = "active",
archived = "archived",
}

export enum FeedbackType {
bug = "Bug",
feature = "Feature",
idea = "Idea",
other = "Other",
}

export enum FeedbackStatus {
open = "Open",
inProgress = "In progress",
closed = "Closed",
}
122 changes: 58 additions & 64 deletions pages/dashboard/index/[projectId]/index/index.vue
Original file line number Diff line number Diff line change
@@ -1,86 +1,80 @@
<template>
<main>
<header>
<div
class="col-span-full border-b dark:border-white/10 flex flex-col items-start justify-between gap-x-8 gap-y-4 bg-gray-100 dark:bg-gray-700/10 px-4 py-4 sm:flex-row sm:items-center sm:px-6 lg:px-8"
>
<div>
<div class="flex items-center gap-x-3">
<UAvatar
src="https://cdn.dribbble.com/assets/dribbble-ball-192-23ecbdf987832231e87c642bb25de821af1ba6734a626c8c259a20a0ca51a247.png"
size="2xs"
/>
<h1 class="flex gap-x-3 text-base leading-7">
<span class="font-semibold">Dribbble</span>
</h1>
</div>
<p class="mt-1 text-xs leading-6 text-gray-500">
Feedback collected from the Dribble website
</p>
</div>
<UButton
label="Add"
icon="i-heroicons-plus-circle"
color="white"
></UButton>
</div>
<div
class="grid grid-cols-2 lg:grid-cols-6 gap-px bg-gray-200 dark:bg-white/10"
>
<div
v-for="stat in stats"
:key="stat.name"
class="py-4 sm:py-6 px-4 sm:px-6 lg:px-8 bg-gray-50 dark:bg-gray-950"
>
<p
class="text-sm font-medium leading-6 text-gray-600 dark:text-gray-400"
>
{{ stat.name }}
</p>
<p class="mt-1 sm:mt-2 flex items-baseline gap-x-2">
<span
class="text-2xl lg:text-4xl font-semibold tracking-tight text-gray-700 dark:text-white/70"
>{{ stat.value }}</span
>
<span
v-if="stat.unit"
class="text-sm text-gray-600 dark:text-gray-400"
>{{ stat.unit }}</span
>
</p>
</div>
</div>
</header>
<DashboardProjectsHeader
:project="project.project"
:stats="project.stats"
/>

<!-- Activity list -->
<div class="border-t dark:border-white/10 pt-11">
<h2 class="px-4 text-base font-semibold leading-7 sm:px-6 lg:px-8">
Latest activity
</h2>
<div>
<UButton @click="isOpen = true">Open</UButton>
<div class="pt-4">
<ul role="list" class="divide-y divide-gray-200 dark:divide-white/10">
<li
v-for="feedback in project.feedbacks"
:key="feedback.id"
class="relative grid grid-cols-8 gap-x-6 px-4 py-5 hover:bg-gray-100 dark:hover:bg-gray-900 sm:px-6 lg:px-8"
>
<div class="min-w-0 flex-grow col-span-6">
<p class="truncate font-mono text-sm leading-6">
{{ feedback.feedback }}
</p>
</div>
<div class="flex items-center gap-x-2 col-span-4 sm:col-span-1">
<div
:class="[
statuses[feedback.category].class,
'flex-none rounded-full p-1',
]"
>
<div class="h-1.5 w-1.5 rounded-full bg-current" />
</div>
<div class="text-sm sm:block">
{{ statuses[feedback.category].label }}
</div>
</div>
<div class="text-sm sm:block col-span-4 sm:col-span-1">
{{ timeAgo(feedback.createdAt) }}
</div>
</li>
</ul>
<!-- <UButton @click="isOpen = true">Open</UButton> -->
<USlideover v-model="isOpen" :ui="feedbackStylesBase">
<DashboardFeedbackDetails />
<DashboardFeedbackDetails
@toggleFeedbackDetails="toggleFeedbackDetails"
/>
</USlideover>
</div>
</div>
</main>
</template>

<script setup>
import { formatTimeAgo } from "@vueuse/core";
const route = useRoute();
const { projectId } = route.params;
const { data: project } = useFetch(`/api/projects/${projectId}`);
const stats = [
{ name: "Total feedbacks", value: "405" },
{ name: "Issues", value: "134" },
{ name: "Ideas", value: "233" },
{ name: "Other", value: "90" },
{ name: "Open", value: "112" },
{ name: "Closed", value: "335" },
];
const { data: project } = useFetch(`/api/projects/${projectId}/overview`);
const feedbackStylesBase = {
base: "relative flex-1 flex flex-col w-full focus:outline-none m-2 rounded-xl",
};
const isOpen = ref(false);
const toggleFeedbackDetails = () => (isOpen.value = !isOpen.value);
const timeAgo = (date) => formatTimeAgo(new Date(date));
const statuses = {
idea: {
class: "text-sky-400 bg-sky-400/10",
label: "Idea",
},
issue: {
class: "text-rose-400 bg-rose-400/10",
label: "Issue",
},
other: {
class: "text-yellow-400 bg-yellow-400/10",
label: "Other",
},
};
</script>
85 changes: 85 additions & 0 deletions server/api/projects/[id]/overview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { and, eq, sql, desc } from "drizzle-orm";
import {
getFeedbackCountOfProject,
feedbackCountByStatus,
feedbackCountByCategory,
countForStatus,
} from "@/server/db/query/analytics";

import { getFeedbacks } from "@/server/db/query/feedback";
import { getProject } from "@/server/db/query/project";
import { Feedback, Project } from "@/lib/types/project";

export default eventHandler(async (event) => {
const session = await requireUserSession(event);
const userId = session.user.id;

const { getProjectId } = useValidation(event);
const projectId = await getProjectId();

let filterBy: any = and(
eq(tables.feedbacks.userId, userId),
eq(tables.feedbacks.projectId, projectId)
);

const feedbackCount = await getFeedbackCountOfProject(filterBy);
const countByStatusQs = await feedbackCountByStatus(filterBy);
const countByCategoryQs = await feedbackCountByCategory(filterBy);

let countByStatus: any = {};
for (const entry of countByStatusQs) {
const { status, count } = entry;
countByStatus[status] = count;
}

let countByCategory: any = {};
for (const entry of countByCategoryQs) {
const { category, count } = entry;
countByCategory[category] = count;
}

const feedbacks: Feedback[] = await getFeedbacks(
{
id: tables.feedbacks.id,
userId: tables.feedbacks.userId,
userEmail: tables.feedbacks.userEmail,
userName: tables.feedbacks.userName,
category: tables.feedbacks.category,
projectId: tables.feedbacks.projectId,
feedback: tables.feedbacks.feedback,
status: tables.feedbacks.status,
createdAt: tables.feedbacks.createdAt,
updatedAt: tables.feedbacks.updatedAt,
},
filterBy,
desc(tables.feedbacks.updatedAt),
0,
20
);

const project: Project = await getProject(projectId);

const result = {
stats: {
feedbackCount: feedbackCount.length,
...countByStatus,
...countByCategory,
},
feedbacks,
project
};

return result;
});

// Weekly calculation
// SELECT idx, COUNT(idx)
// FROM
// (SELECT ((created_at-min)/(60*60*24*7)) AS idx
// FROM
// (SELECT min(created_at) AS min
// FROM feedbacks)
// LEFT JOIN feedbacks WHERE created_at NOT NULL)
// GROUP BY idx;

// SELECT strftime('%Y-%W', datetime(created_at,'unixepoch')) AS w, count(1) FROM feedbacks GROUP BY w;
Loading

0 comments on commit 6d731a8

Please sign in to comment.