Browse Source

Merge pull request #2723 from nocodb/feat/project-list

Feat/project list
pull/2728/head
Pranav C 2 years ago committed by GitHub
parent
commit
5142a15f2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      packages/nc-gui-v2/assets/style-v2.scss
  2. 2
      packages/nc-gui-v2/components.d.ts
  3. 8
      packages/nc-gui-v2/components/cell/Url.vue
  4. 14
      packages/nc-gui-v2/components/dashboard/TreeView.vue
  5. 53
      packages/nc-gui-v2/pages/index/index.vue
  6. 42
      packages/nc-gui-v2/pages/index/index/index.vue
  7. 18
      packages/nc-gui-v2/pages/index/index/list.vue
  8. 4
      packages/nc-gui-v2/pages/nc/[projectId].vue
  9. 0
      packages/nc-gui-v2/pages/projects/create-external.vue
  10. 0
      packages/nc-gui-v2/pages/projects/create.vue
  11. 150
      packages/nc-gui-v2/pages/projects/index.vue

6
packages/nc-gui-v2/assets/style-v2.scss

@ -63,4 +63,8 @@ h1, h2, h3, h4, h5, h6 {
.nc-icon {
@apply color-transition;
}
}
:root {
--header-height: 64px;
}

2
packages/nc-gui-v2/components.d.ts vendored

@ -7,6 +7,7 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
ADivider: typeof import('ant-design-vue/es')['Divider']
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
ALayout: typeof import('ant-design-vue/es')['Layout']
ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
@ -15,6 +16,7 @@ declare module '@vue/runtime-core' {
AMenu: typeof import('ant-design-vue/es')['Menu']
AMenuDivider: typeof import('ant-design-vue/es')['MenuDivider']
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
AModal: typeof import('ant-design-vue/es')['Modal']
ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
ATable: typeof import('ant-design-vue/es')['Table']
RouterLink: typeof import('vue-router')['RouterLink']

8
packages/nc-gui-v2/components/cell/Url.vue

@ -3,15 +3,15 @@ import { computed, ref } from '#imports'
import { ColumnInj } from '~/components'
import { isValidURL } from '~/utils/urlUtils'
interface Props {
modelValue: string
}
const { modelValue: value } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const column = inject(ColumnInj)
const editEnabled = inject<boolean>('editEnabled')
interface Props {
modelValue: string
}
const localState = computed({
get: () => value,
set: (val) => {

14
packages/nc-gui-v2/components/dashboard/TreeView.vue

@ -7,17 +7,17 @@ const { addTab } = useTabs()
</script>
<template>
<div>
<v-list>
<v-list-item
<div class="nc-treeview-container flex flex-column">
<a-menu>
<a-menu-item
v-for="table in tables"
:key="table.id"
class="p-2 text-sm pointer"
@click="addTab({ type: 'table', title: table.title, id: table.id })"
>
{{ table.title }}
</v-list-item>
</v-list>
</a-menu-item>
</a-menu>
</div>
</template>
@ -25,4 +25,8 @@ const { addTab } = useTabs()
.pointer {
cursor: pointer;
}
.nc-treeview-container {
height: calc(100vh - var(--header-height));
}
</style>

53
packages/nc-gui-v2/pages/index/index.vue

@ -1,40 +1,65 @@
<script lang="ts" setup>
import { createVNode } from '@vue/runtime-core'
import { Modal } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk'
import { useToast } from 'vue-toastification'
import { navigateTo } from '#app'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
import MaterialSymbolsFormatListBulletedRounded from '~icons/material-symbols/format-list-bulleted-rounded'
import MaterialSymbolsGridView from '~icons/material-symbols/grid-view'
import MdiPlus from '~icons/mdi/plus'
import MdiDatabaseOutline from '~icons/mdi/database-outline'
import MdiFolderOutline from '~icons/mdi/folder-outline'
import MdiAccountGroup from '~icons/mdi/account-group'
import MdiClockOutline from '~icons/mdi/clock-outline'
import MdiStar from '~icons/mdi/star'
import ExclamationCircleOutlined from '~icons/mdi/information-outline'
const navDrawerOptions = [
{
title: 'My NocoDB',
icon: MdiFolderOutline,
},
{
title: 'Shared With Me',
icon: MdiAccountGroup,
/* todo: implement the api and bring back the options below
{
title: "Shared With Me",
icon: MdiAccountGroup
},
{
title: 'Recent',
icon: MdiClockOutline,
title: "Recent",
icon: MdiClockOutline
},
{
title: 'Starred',
icon: MdiStar,
},
title: "Starred",
icon: MdiStar
} */
]
const route = useRoute()
const { $api, $state } = useNuxtApp()
const toast = useToast()
const response = await $api.project.list({})
const projects = $ref(response.list)
const activePage = $ref(navDrawerOptions[0].title)
const deleteProject = (project: ProjectType) => {
Modal.confirm({
title: 'Do you want to delete the project?',
// icon: createVNode(ExclamationCircleOutlined),
content: 'Some descriptions',
okText: 'Yes',
okType: 'danger',
cancelText: 'No',
async onOk() {
try {
await $api.project.delete(project.id as string)
projects.splice(projects.indexOf(project), 1)
} catch (e) {
toast.error(await extractSdkResponseErrorMsg(e))
}
},
})
}
const visible = ref(true)
</script>
<template>
@ -115,9 +140,11 @@ const activePage = $ref(navDrawerOptions[0].title)
</div>
</div>
<v-divider class="!mb-4 lg:(!mb-8)" />
<a-divider class="!mb-4 lg:(!mb-8)" />
<NuxtPage :projects="projects" />
<NuxtPage :projects="projects" @delete-project="deleteProject" />
</v-container>
<a-modal></a-modal>
</NuxtLayout>
</template>

42
packages/nc-gui-v2/pages/index/index/index.vue

@ -2,11 +2,11 @@
import type { ProjectType } from 'nocodb-sdk'
import { navigateTo } from '#app'
import useColors from '~/composables/useColors'
import MdiStarOutline from '~icons/mdi/star-outline'
import MdiMenuDown from '~icons/mdi/menu-down'
import MdiDeleteOutline from '~icons/mdi/delete-outline'
import MdiPlus from '~icons/mdi/plus'
import MdiDatabaseOutline from '~icons/mdi/database-outline'
import MdiEditOutline from '~icons/mdi/edit-outline'
interface Props {
projects: ProjectType[]
@ -14,6 +14,8 @@ interface Props {
const { projects } = defineProps<Props>()
const emit = defineEmits(['delete-project'])
const { $e } = useNuxtApp()
const { getColorByIndex } = useColors(true)
@ -48,10 +50,7 @@ const formatTitle = (title: string) =>
</div>
</template>
<v-list class="!py-0 flex flex-col bg-white rounded-lg shadow-md border-1 border-gray-300 mt-2 ml-2">
<div
class="grid grid-cols-12 cursor-pointer hover:bg-gray-200 flex items-center p-2"
@click="navigateTo('/create')"
>
<div class="grid grid-cols-12 cursor-pointer hover:bg-gray-200 flex items-center p-2" @click="navigateTo('/create')">
<MdiPlus class="col-span-2 mr-1 mt-[1px] text-primary text-lg" />
<div class="col-span-10 text-sm xl:text-md">{{ $t('activity.createProject') }}</div>
</div>
@ -69,21 +68,25 @@ const formatTitle = (title: string) =>
<div v-for="(project, i) of projects" :key="project.id" class="group flex flex-col items-center gap-2">
<div class="thumbnail" :style="{ '--thumbnail-color': getColorByIndex(i) }" @click="openProject(project)">
{{ formatTitle(project.title) }}
<MdiStarOutline class="star-icon" @click.stop />
<v-menu>
<template #activator="{ props }">
<MdiMenuDown class="menu-icon" @click.stop="props.onClick" />
<a-dropdown @click.stop>
<MdiMenuDown class="menu-icon" />
<template #overlay>
<a-menu>
<a-menu-item @click.stop="emit('delete-project', project)">
<div class="grid grid-cols-6 cursor-pointer flex items-center p-2">
<MdiDeleteOutline class="col-span-2 mr-1 mt-[1px] text-red text-lg" />
<div class="col-span-4 text-sm xl:text-md">{{ $t('general.delete') }}</div>
</div>
</a-menu-item>
<a-menu-item>
<div class="grid grid-cols-6 cursor-pointer flex items-center p-2">
<MdiEditOutline class="col-span-2 mr-1 mt-[1px] text-primary text-lg" />
<div class="col-span-4 text-sm xl:text-md">{{ $t('general.edit') }}</div>
</div>
</a-menu-item>
</a-menu>
</template>
<v-list class="!py-0 flex flex-col bg-white rounded-lg shadow-md border-1 border-gray-300">
<div class="grid grid-cols-6 cursor-pointer hover:bg-gray-200 flex items-center p-2" @click.stop>
<MdiDeleteOutline class="col-span-2 mr-1 mt-[1px] text-red text-lg" />
<div class="col-span-4 text-sm xl:text-md">{{ $t('general.delete') }}</div>
</div>
</v-list>
</v-menu>
</a-dropdown>
</div>
<div class="prose-lg font-semibold">
@ -104,6 +107,7 @@ const formatTitle = (title: string) =>
content: '';
z-index: -1;
}
.thumbnail:hover::after {
@apply shadow-2xl transform scale-110;
}

18
packages/nc-gui-v2/pages/index/index/list.vue

@ -2,12 +2,17 @@
import type { ProjectType } from 'nocodb-sdk'
import { navigateTo } from '#app'
import MdiDeleteOutline from '~icons/mdi/delete-outline'
import MdiEditOutline from '~icons/mdi/edit-outline'
interface Props {
projects: ProjectType[]
}
const { projects } = defineProps<Props>()
const emit = defineEmits(['delete-project'])
const { $e } = useNuxtApp()
const openProject = async (project: ProjectType) => {
@ -17,23 +22,26 @@ const openProject = async (project: ProjectType) => {
</script>
<template>
<div class="mx-8">
<div class="grid grid-cols-3 gap-2 prose-md p-2 font-semibold">
<div class="mx-auto max-w-[700px]">
<div class="grid grid-cols-4 gap-2 prose-md p-2 font-semibold">
<div>{{ $t('general.title') }}</div>
<div>Status</div>
<div>Updated At</div>
<div></div>
</div>
<v-divider class="col-span-3" />
<template v-for="project of projects" :key="project.id">
<div
class="cursor-pointer grid grid-cols-3 gap-2 prose-md hover:(bg-gray-100 shadow-sm dark:text-black) p-2"
class="cursor-pointer grid grid-cols-4 gap-2 prose-md hover:(bg-gray-100 shadow-sm dark:text-black) p-2 transition-color ease-in duration-100"
@click="openProject(project)"
>
<div class="font-semibold">{{ project.title || 'Untitled' }}</div>
<div>{{ project.status }}</div>
<div>{{ project.updated_at }}</div>
<div>
<MdiDeleteOutline class="text-gray-500 hover:text-red-500 mr-2" @click.stop @click="emit('delete-project', project)" />
<MdiEditOutline class="text-gray-500 hover:text-primary mr-2" @click.stop />
</div>
</div>
<v-divider class="col-span-3" />
</template>

4
packages/nc-gui-v2/pages/nc/[projectId].vue

@ -29,9 +29,7 @@ watch(
<template>
<NuxtLayout>
<template #sidebar>
<v-navigation-drawer permanent>
<DashboardTreeView />
</v-navigation-drawer>
<DashboardTreeView />
</template>
<v-container fluid>

0
packages/nc-gui-v2/pages/index/create-external.vue → packages/nc-gui-v2/pages/projects/create-external.vue

0
packages/nc-gui-v2/pages/index/create.vue → packages/nc-gui-v2/pages/projects/create.vue

150
packages/nc-gui-v2/pages/projects/index.vue

@ -0,0 +1,150 @@
<script lang="ts" setup>
import { createVNode } from '@vue/runtime-core'
import { Modal } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk'
import { useToast } from 'vue-toastification'
import { navigateTo } from '#app'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
import MaterialSymbolsFormatListBulletedRounded from '~icons/material-symbols/format-list-bulleted-rounded'
import MaterialSymbolsGridView from '~icons/material-symbols/grid-view'
import MdiPlus from '~icons/mdi/plus'
import MdiDatabaseOutline from '~icons/mdi/database-outline'
import MdiFolderOutline from '~icons/mdi/folder-outline'
import ExclamationCircleOutlined from '~icons/mdi/information-outline'
const navDrawerOptions = [
{
title: 'My NocoDB',
icon: MdiFolderOutline,
},
/* todo: implement the api and bring back the options below
{
title: "Shared With Me",
icon: MdiAccountGroup
},
{
title: "Recent",
icon: MdiClockOutline
},
{
title: "Starred",
icon: MdiStar
} */
]
const route = useRoute()
const { $api, $state } = useNuxtApp()
const toast = useToast()
const response = await $api.project.list({})
const projects = $ref(response.list)
const activePage = $ref(navDrawerOptions[0].title)
const deleteProject = (project: ProjectType) => {
Modal.confirm({
title: 'Do you want to delete the project?',
// icon: createVNode(ExclamationCircleOutlined),
content: 'Some descriptions',
okText: 'Yes',
okType: 'danger',
cancelText: 'No',
async onOk() {
try {
await $api.project.delete(project.id as string)
projects.splice(projects.indexOf(project), 1)
} catch (e) {
toast.error(await extractSdkResponseErrorMsg(e))
}
},
})
}
const visible = ref(true)
</script>
<template>
<NuxtLayout>
<template #sidebar>
<div class="flex flex-col h-full">
<div class="flex p-4">
<v-menu class="select-none">
<template #activator="{ props }">
<div
class="color-transition hover:(bg-gray-100 dark:bg-secondary/25) dark:(bg-secondary/50 !text-white shadow-gray-600) mr-auto select-none flex items-center gap-2 leading-8 cursor-pointer rounded-full border-1 border-gray-300 px-5 py-2 shadow prose-lg font-semibold"
@click="props.onClick"
>
<MdiPlus class="text-primary dark:(!text-white) text-2xl" />
{{ $t('title.newProj') }}
</div>
</template>
<v-list class="!py-0 flex flex-col bg-white rounded-lg shadow-md border-1 border-gray-300 mt-2 ml-2">
<div
class="grid grid-cols-12 cursor-pointer hover:bg-gray-200 flex items-center p-2"
@click="navigateTo('/create')"
>
<MdiPlus class="col-span-2 mr-1 mt-[1px] text-primary text-lg" />
<div class="col-span-10 text-sm xl:text-md">{{ $t('activity.createProject') }}</div>
</div>
<div
class="grid grid-cols-12 cursor-pointer hover:bg-gray-200 flex items-center p-2"
@click="navigateTo('/create-external')"
>
<MdiDatabaseOutline class="col-span-2 mr-1 mt-[1px] text-green-500 text-lg" />
<div class="col-span-10 text-sm xl:text-md" v-html="$t('activity.createProjectExtended.extDB')" />
</div>
</v-list>
</v-menu>
</div>
<a-menu class="mx-4 dark:bg-gray-800 dark:text-white flex-1 border-0">
<a-menu-item
v-for="(option, index) in navDrawerOptions"
:key="index"
class="f!rounded-r-lg"
@click="activePage = option.title"
>
<div class="flex items-center gap-4">
<component :is="option.icon" />
<span class="font-semibold">
{{ option.title }}
</span>
</div>
</a-menu-item>
</a-menu>
<general-social />
<general-sponsors :nav="true" />
</div>
</template>
<v-container class="flex-1 mb-12">
<div class="flex">
<div class="flex-1 text-2xl md:text-4xl font-bold text-gray-500 dark:text-white p-4">
{{ activePage }}
</div>
<div class="self-end flex text-4xl mb-1">
<MaterialSymbolsGridView
:class="route.name === 'projects-index' ? 'text-primary dark:(!text-secondary/75)' : ''"
class="color-transition cursor-pointer p-2 hover:bg-gray-300/50 rounded-full"
@click="navigateTo('/projects')"
/>
<MaterialSymbolsFormatListBulletedRounded
:class="route.name === 'projects-index-list' ? 'text-primary dark:(!text-secondary/75)' : ''"
class="color-transition cursor-pointer p-2 hover:bg-gray-300/50 rounded-full"
@click="navigateTo('/projects/list')"
/>
</div>
</div>
<a-divider class="!mb-4 lg:(!mb-8)" />
<NuxtPage :projects="projects" @delete-project="deleteProject" />
</v-container>
<a-modal></a-modal>
</NuxtLayout>
</template>
Loading…
Cancel
Save