Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workspaces #129

Merged
merged 31 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f830f25
Initial DB migrations for workspaces & plans
raggesilver Oct 14, 2024
d81a881
Sync
raggesilver Oct 14, 2024
6d17512
Fix landing page title and open attachment in new tab
raggesilver Oct 22, 2024
443fcf5
Initial work on new TOS and Privacy Policy
raggesilver Oct 23, 2024
3859e27
Add workspace creation functionality and enhance UI
raggesilver Oct 25, 2024
b5d25af
Initial work on workspace settings
raggesilver Oct 27, 2024
f4228ef
Prevent unnecessary workspace updates and enhance error handling
raggesilver Oct 28, 2024
9834857
Add new components, improve workspace settings and fix types
raggesilver Oct 28, 2024
b6fc13d
Initial work on workspace collaborators
raggesilver Nov 6, 2024
876f6f6
Sync
raggesilver Dec 27, 2024
5d755aa
Fix workspace invitation link state
raggesilver Dec 27, 2024
0b513e1
Disable Posthog plugin during prerender
raggesilver Dec 27, 2024
9f0988a
Update dependencies
raggesilver Dec 27, 2024
8db7032
Please work
raggesilver Dec 27, 2024
c3751ed
Update deps again
raggesilver Dec 27, 2024
9edaa56
Clean storage + Local MinIO
raggesilver Dec 29, 2024
7300001
Workspace usage limits API + UI + Redis
raggesilver Dec 30, 2024
085e8a8
Improve workspace UI and UTF-8 attachment names
raggesilver Dec 30, 2024
2c19f10
Fix logic for various workspace vs board collaborator authorization c…
raggesilver Dec 31, 2024
1ac8f5b
Show workspace collaborator list in workspace settings
raggesilver Dec 31, 2024
7c0940f
Remove workspace collaborators
raggesilver Jan 3, 2025
8b215f7
Fix task not editable after creation
raggesilver Jan 3, 2025
59326be
Update deps
raggesilver Jan 3, 2025
35e17cb
Replace ts-expect-error with ts-ignore
raggesilver Jan 3, 2025
8328d8d
Make redis connection lazy (fixes broken build)
raggesilver Jan 3, 2025
d35db58
Disable sentry ESM wrappers
raggesilver Jan 3, 2025
966cae8
Use IPv6 for Redis in prod
raggesilver Jan 3, 2025
b6c4032
Better usage metrics
raggesilver Jan 4, 2025
a9375e1
Migrate to new Drizzle API
raggesilver Jan 8, 2025
d2520af
Optional task descriptions
raggesilver Jan 8, 2025
9b6eb44
Cleanup + TOS & Privacy Policy release date
raggesilver Jan 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/fly-staging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Fly Deploy (Staging)
on:
push:
branches:
- staging

env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

jobs:
deploy:
name: Deploy app
runs-on: ubuntu-latest
concurrency: deploy-group-staging
environment:
name: staging
url: https://tasksapp-staging.fly.dev/
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl config save --app tasksapp-staging --config fly-staging.toml
- run: flyctl deploy --config fly-staging.toml --remote-only
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ logs
.env.*
!.env.example

/db-data
/db-data*
/cert
/coverage
1 change: 1 addition & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"trailingComma": "all",
"plugins": [
"prettier-plugin-organize-imports",
"prettier-plugin-jsdoc",
"prettier-plugin-sql"
],
"language": "postgresql"
Expand Down
4 changes: 4 additions & 0 deletions app/assets/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ body {
@apply bg-background text-foreground;
}

.standard-grid {
@apply grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 auto-rows-fr;
}

.list-move, /* apply transition to moving elements */
.list-enter-active,
.list-leave-active {
Expand Down
3 changes: 3 additions & 0 deletions app/components/activity-indicator.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<Icon name="lucide:loader" class="animate-spin" />
</template>
8 changes: 5 additions & 3 deletions app/components/app-breadcrumbs/index.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
defineProps<{
entries: { title: string; link: string }[];
entries: { title: string; link: string; style?: string }[];
}>();
</script>

Expand All @@ -11,11 +11,13 @@ defineProps<{
<BreadcrumbSeparator v-if="i > 0" />
<BreadcrumbItem>
<BreadcrumbLink v-if="i < entries.length - 1" as-child>
<NuxtLink :to="entry.link">
<NuxtLink :to="entry.link" :style="entry.style">
{{ entry.title }}
</NuxtLink>
</BreadcrumbLink>
<BreadcrumbPage v-else>{{ entry.title }}</BreadcrumbPage>
<BreadcrumbPage v-else :style="entry.style">{{
entry.title
}}</BreadcrumbPage>
</BreadcrumbItem>
</template>
</BreadcrumbList>
Expand Down
6 changes: 5 additions & 1 deletion app/components/attachment/preview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,11 @@ const iconForFileType = computed(() => {
</EasyTooltip>
<EasyTooltip tooltip="Open in new tab">
<Button size="micro" variant="outline" as-child>
<NuxtLink :to="`/api/attachment/${attachment.id}`" external>
<NuxtLink
:to="`/api/attachment/${attachment.id}`"
external
target="_blank"
>
<Icon name="lucide:external-link" class="w-3 h-3" />
</NuxtLink>
</Button>
Expand Down
21 changes: 21 additions & 0 deletions app/components/coming-soon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script lang="ts" setup>
defineProps<{
withIcon?: boolean;
large?: boolean;
vertical?: boolean;
}>();
</script>

<template>
<span
:class="{ 'text-xl': large, 'flex-col': vertical }"
class="flex gap-x-2 gap-y-0 items-center"
>
<Icon
v-if="withIcon"
:class="large ? 'text-[2em]' : 'text-[1.4em]'"
name="lucide:hard-hat"
/>
<span>Coming soon.</span>
</span>
</template>
13 changes: 13 additions & 0 deletions app/components/content/privacy-release-date.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
const lastReleaseDate = "2024-08-27";

const production = ref(process.env.NODE_ENV === "production");

const releaseDate = computed(() =>
production.value ? lastReleaseDate : "RELEASE_DATE",
);
</script>

<template>
<span>{{ releaseDate }}</span>
</template>
13 changes: 13 additions & 0 deletions app/components/content/tos-release-date.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
const lastReleaseDate = "2024-08-27";

const production = ref(process.env.NODE_ENV === "production");

const releaseDate = computed(() =>
production.value ? lastReleaseDate : "RELEASE_DATE",
);
</script>

<template>
<span>{{ releaseDate }}</span>
</template>
25 changes: 22 additions & 3 deletions app/components/create-board/form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,22 @@ import { useForm } from "vee-validate";
import { toast } from "vue-sonner";
import type { z } from "zod";
import { createBoardSchema } from "~/lib/validation";
import type { Board } from "~~/server/db/schema";
import type { Board, Workspace } from "~~/server/db/schema";

const props = defineProps<{
workspace: Workspace;
}>();

const emit = defineEmits(["dismiss"]);

const schema = toTypedSchema(createBoardSchema);

const form = useForm({
validationSchema: schema,
initialValues: {
workspaceId: props.workspace.id,
name: "",
},
});

const queryClient = useQueryClient();
Expand Down Expand Up @@ -66,20 +74,31 @@ const onSubmit = form.handleSubmit((values) => {

<template>
<form class="p-4 pb-2 sm:p-0" @submit="onSubmit">
<FormField v-slot="{ componentField }" name="workspaceId">
<FormItem>
<FormControl>
<Input type="hidden" v-bind="componentField" disabled />
</FormControl>
</FormItem>
</FormField>
<FormField v-slot="{ componentField }" name="name">
<FormItem>
<FormLabel>Name</FormLabel>
<FormLabel>Board Name</FormLabel>
<FormControl>
<Input
type="text"
placeholder="JavaScript 101"
v-bind="componentField"
/>
</FormControl>
<FormDescription>This is the name of your board.</FormDescription>
<FormMessage />
</FormItem>
</FormField>

<p class="mt-4 text-sm text-muted-foreground text-left">
The new board will inherit settings from the workspace, meaning workspace
members will have access to it.
</p>
<p v-if="formError" class="mt-4 text-[0.8rem] font-medium text-destructive">
{{ formError }}
</p>
Expand Down
17 changes: 12 additions & 5 deletions app/components/create-board/index.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
<script lang="ts" setup>
import { useMediaQuery } from "@vueuse/core";
import type { Workspace } from "~~/server/db/schema";
import Form from "./form.vue";

const props = defineProps<{
workspace: Workspace;
}>();

const isDesktop = useMediaQuery("(min-width: 640px)");

const isOpen = ref(false);

const title = "Create Board";
const description = "Create a new board to organize your tasks.";
const title = "Create a new board";
const description = computed(
() => `Create a new board in the ${props.workspace.name} workspace.`,
);
</script>

<template>
Expand All @@ -19,7 +26,7 @@ const description = "Create a new board to organize your tasks.";
<DialogTitle>{{ title }}</DialogTitle>
<DialogDescription>{{ description }}</DialogDescription>
</DialogHeader>
<Form @dismiss="isOpen = false" />
<Form :workspace @dismiss="isOpen = false" />
</DialogContent>
</Dialog>
<!-- And an iOS-like bottom sheet on mobile -->
Expand All @@ -29,7 +36,7 @@ const description = "Create a new board to organize your tasks.";
<DrawerTitle>{{ title }}</DrawerTitle>
<DrawerDescription>{{ description }}</DrawerDescription>
</DrawerHeader>
<Form @dismiss="isOpen = false" />
<Form :workspace @dismiss="isOpen = false" />
<DrawerFooter class="pt-2">
<DrawerClose as-child>
<Button variant="outline">Cancel</Button>
Expand All @@ -40,7 +47,7 @@ const description = "Create a new board to organize your tasks.";
</ClientOnly>

<Button
variant="outline"
variant="secondary"
size="sm"
class="flex items-center gap-2"
@click="isOpen = true"
Expand Down
20 changes: 13 additions & 7 deletions app/components/create-column/form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,31 @@ import { useForm } from "vee-validate";
import { toast } from "vue-sonner";
import type { z } from "zod";
import { createStatusColumnSchema } from "~/lib/validation";
import type { StatusColumn } from "~~/server/db/schema";
import type { Board, StatusColumn } from "~~/server/db/schema";

const localSchema = createStatusColumnSchema.pick({ name: true });
type SchemaType = z.infer<typeof localSchema>;
const props = defineProps<{
board: Board;
}>();

type SchemaType = z.infer<typeof createStatusColumnSchema>;

const boardId = useRouteParamSafe("id") as Ref<string>;
const emit = defineEmits(["dismiss"]);

const schema = toTypedSchema(localSchema);
const schema = toTypedSchema(createStatusColumnSchema);

const form = useForm({
validationSchema: schema,
initialValues: {
name: "",
workspaceId: props.board.workspaceId,
},
});

const queryClient = useQueryClient();

const { mutateAsync } = useMutation({
mutationFn: (data: SchemaType) =>
$fetch(`/api/column/${boardId.value}`, {
$fetch(`/api/column/${props.board.id}`, {
method: "POST",
body: data,
}),
Expand All @@ -38,7 +44,7 @@ const { mutateAsync } = useMutation({
normalized,
);
queryClient.setQueryData<StatusColumn[]>(
["board-columns", boardId],
["board-columns", props.board.id],
(old) => {
if (old) {
return [...old, normalized];
Expand Down
13 changes: 9 additions & 4 deletions app/components/create-column/index.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
<script lang="ts" setup>
import { useMediaQuery } from "@vueuse/core";
import type { Board } from "~~/server/db/schema";
import Form from "./form.vue";

defineProps<{
board: Board;
}>();

const isDesktop = useMediaQuery("(min-width: 640px)");

const isOpen = ref(false);
Expand All @@ -19,7 +24,7 @@ const description = "Create a new status column to organize your tasks.";
<DialogTitle>{{ title }}</DialogTitle>
<DialogDescription>{{ description }}</DialogDescription>
</DialogHeader>
<Form @dismiss="isOpen = false" />
<Form :board @dismiss="isOpen = false" />
</DialogContent>
</Dialog>
<!-- And an iOS-like bottom sheet on mobile -->
Expand All @@ -29,7 +34,7 @@ const description = "Create a new status column to organize your tasks.";
<DrawerTitle>{{ title }}</DrawerTitle>
<DrawerDescription>{{ description }}</DrawerDescription>
</DrawerHeader>
<Form @dismiss="isOpen = false" />
<Form :board @dismiss="isOpen = false" />
<DrawerFooter class="pt-2">
<DrawerClose as-child>
<Button variant="outline">Cancel</Button>
Expand All @@ -39,7 +44,7 @@ const description = "Create a new status column to organize your tasks.";
</Drawer>
</ClientOnly>

<Button variant="outline" @click="isOpen = true">
New Column <Icon name="lucide:plus" />
<Button variant="secondary" size="sm" @click="isOpen = true">
New Column <Icon name="lucide:plus" class="h-[1em] ml-1" />
</Button>
</template>
19 changes: 19 additions & 0 deletions app/components/create-workspace/description.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script lang="ts" setup>
import { WORKSPACE_DEFINITION } from "~/lib/constants";

const tooltip = `${WORKSPACE_DEFINITION} Each workspace has its own plan and settings.`;
</script>

<template>
<span v-once
>Create a new
<EasyTooltip :tooltip>
<span
class="underline decoration-dashed decoration-2 decoration-offset-4 cursor-help"
>
workspace</span
>
</EasyTooltip>
to organize your projects.</span
>
</template>
Loading
Loading