Skip to content

Commit

Permalink
Merge pull request #22 from doroudi:doroudi/issue17
Browse files Browse the repository at this point in the history
Implement Add Category
  • Loading branch information
doroudi authored Nov 17, 2023
2 parents d806031 + d819a62 commit 3b1b82b
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 152 deletions.
6 changes: 5 additions & 1 deletion locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,15 @@ forgot:
dashboard:
summary: Summary
transactions: Transactions
category:
categories:
title: Categories
createButton: Create
create:
buttonTitle: Create
title: Create New Category
categoryName: Name
parent: Parent
validations:
nameRequired: Name is required
parentRequired: Parent Not Selected
not-found: Not found
4 changes: 4 additions & 0 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ declare module 'vue' {
export interface GlobalComponents {
BarChart: typeof import('./components/BarChart.vue')['default']
Card: typeof import('./components/Card.vue')['default']
CategoryManagement: typeof import('./components/Category/CategoryManagement.vue')['default']
CategoryStatics: typeof import('./components/Category/CategoryStatics.vue')['default']
CreateCategory: typeof import('./components/Category/CreateCategory.vue')['default']
DashboardCard: typeof import('./components/DashboardCard.vue')['default']
Expand All @@ -23,6 +24,8 @@ declare module 'vue' {
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
NDrawer: typeof import('naive-ui')['NDrawer']
NDrawerContent: typeof import('naive-ui')['NDrawerContent']
NForm: typeof import('naive-ui')['NForm']
NFormItem: typeof import('naive-ui')['NFormItem']
NIcon: typeof import('naive-ui')['NIcon']
NInput: typeof import('naive-ui')['NInput']
NLayout: typeof import('naive-ui')['NLayout']
Expand All @@ -33,6 +36,7 @@ declare module 'vue' {
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
NPageHeader: typeof import('naive-ui')['NPageHeader']
NPopselect: typeof import('naive-ui')['NPopselect']
NSelect: typeof import('naive-ui')['NSelect']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Sidebar: typeof import('./components/Sidebar.vue')['default']
Expand Down
134 changes: 134 additions & 0 deletions src/components/Category/CategoryManagement.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<script setup lang='ts'>
import { type DataTableColumns, NButton, NIcon } from 'naive-ui/es/components'
import type { RowData } from 'naive-ui/es/data-table/src/interface'
import {
DismissCircle24Regular as DeleteIcon,
Edit32Regular as EditIcon,
AddCircle20Regular as PlusIcon,
} from '@vicons/fluent'
import { storeToRefs } from 'pinia'
const { t } = useI18n()
const collapsed = ref(false)
const store = useCategoryStore()
const { categories, isLoading } = storeToRefs(store)
onMounted(getItems)
const columns: DataTableColumns<RowData> = [
{
title: 'Name',
key: 'name',
},
{
title: 'Products Count',
key: 'productsCount',
},
{
title: 'Actions',
key: 'actions',
width: 200,
render(row) {
return [
h(
NButton,
{
size: 'small',
renderIcon: renderIcon(EditIcon),
ghost: true,
class: 'mr-2',
onClick: () => edit(row),
},
{ default: () => 'Edit' },
),
h(
NButton,
{
size: 'small',
type: 'error',
ghost: true,
renderIcon: renderIcon(DeleteIcon),
onClick: () => deleteItem(row),
},
{ default: () => 'Delete' },
),
]
},
},
]
const { options } = storeToRefs(store)
const showAddDialog = ref(false)
function renderIcon(icon: any) {
return () => h(NIcon, null, { default: () => h(icon) })
}
function edit(row: RowData) {

Check warning on line 63 in src/components/Category/CategoryManagement.vue

View workflow job for this annotation

GitHub Actions / lint

'row' is defined but never used. Allowed unused args must match /^_/u
}
function deleteItem(row: RowData) {

Check warning on line 66 in src/components/Category/CategoryManagement.vue

View workflow job for this annotation

GitHub Actions / lint

'row' is defined but never used. Allowed unused args must match /^_/u
}
function rowKey(row: RowData) {
return row.index
}
function getItems() {
store.getCategories(options.value)
}
function handlePageChange(page: number) {
options.value.page = page
getItems()
}
function handleSorterChange() {
getItems()
}
function handleFiltersChange() {
getItems()
}
function createCategory() {
showAddDialog.value = true
}
</script>

<template>
<n-layout has-sider>
<n-layout-content>
<div>
<div class="flex items-center mb-5">
<h1 class="page-title mx-2">
{{ t('categories.title') }}
</h1>
<NButton type="primary" quaternary round @click="createCategory">
<template #icon>
<NIcon>
<PlusIcon />
</NIcon>
</template>
{{ t('categories.createButton') }}
</NButton>
</div>
<n-data-table
remote :columns="columns" :data="categories" :loading="isLoading" :pagination="options"
:row-key="rowKey" @update:sorter="handleSorterChange" @update:filters="handleFiltersChange"
@update:page="handlePageChange"
/>
</div>
</n-layout-content>
<n-layout-sider
bordered collapse-mode="width" :collapsed-width="0" :width="300" :collapsed="collapsed" show-trigger
@collapse="collapsed = true" @expand="collapsed = false"
>
<CategoryStatics />
</n-layout-sider>

<n-drawer v-model:show="showAddDialog" :width="502" placement="right">
<n-drawer-content closable title="Create Category">
<CreateCategory @close="showAddDialog = false" />
</n-drawer-content>
</n-drawer>
</n-layout>
</template>

<style scoped lang='scss'>
</style>
74 changes: 56 additions & 18 deletions src/components/Category/CreateCategory.vue
Original file line number Diff line number Diff line change
@@ -1,37 +1,75 @@
<script setup lang='ts'>
import type { FormInst, FormRules } from 'naive-ui/es/form'
import type { SelectMixedOption } from 'naive-ui/es/select/src/interface'
import { storeToRefs } from 'pinia'
import type { Category } from '~/models/Category'
import type { Category, CategoryCreateModel } from '~/models/Category'
const emits = defineEmits(['close'])
const categoryStore = useCategoryStore()
const { isLoading } = storeToRefs(categoryStore)
const categoryItem = ref<Category>({} as Category)
const { isLoading, categories } = storeToRefs(categoryStore)
const categoryItem = ref<CategoryCreateModel>({ name: '', parentId: 0 })
const { t } = useI18n()
const formRef = ref<FormInst | null>(null)
async function create() {
// TODO: validate
await categoryStore.createCategory(categoryItem.value)
emits('close')
formRef.value?.validate(async (errors: any) => {
console.log('🚀 ~ file: CreateCategory.vue:16 ~ formRef.value?.validate ~ errors:', errors)

Check failure on line 16 in src/components/Category/CreateCategory.vue

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
if (!errors) {
await categoryStore.createCategory(categoryItem.value)
emits('close')
}
})
}
const categoriesOptions = categories.value.map((x: Category) => {
return {
value: x.id,
label: x.name,
}
})
const parents: SelectMixedOption[] = [{ value: 0, label: 'Root' }, ...categoriesOptions]
const nameInput = ref()
onMounted(() => {
nameInput.value?.focus()
})
const rules: FormRules = {
name: [
{
required: true,
trigger: ['blur', 'change'],
message: t('categories.validations.nameRequired'),
},
],
parentId: [
{
required: true,
trigger: ['blur', 'change'],
message: t('categories.validations.parentRequired'),
},
],
}
</script>

<template>
<form @submit.prevent="create()">
<div class="mb-5">
<span class="p-float-label">
<label for="username" class="block font-medium">{{ t('category.create.categoryName') }}</label>
<n-input id="name" :placeholder="t('category.create.categoryName')" />
</span>
<n-form ref="formRef" :model="categoryItem" :rules="rules" @submit.prevent="create()">
<div class="form-control">
<n-form-item class="mb-5" path="name" :label="t('categories.create.categoryName')">
<n-input
id="name" ref="nameInput" v-model:value="categoryItem.name" autofocus
:placeholder="t('categories.create.categoryName')"
/>
</n-form-item>
</div>

<div class="mb-8">
<label for="parent" class="block font-medium">{{ t('category.create.parent') }}</label>
<n-input type="text" :placeholder="t('category.create.parent')" />
<div class="form-control">
<n-form-item class="mb-5" :label="t('categories.create.parent')">
<n-select id="parentId" v-model:value="categoryItem.parentId" :options="parents" :placeholder="t('categories.create.parent')" />
</n-form-item>
</div>

<n-button attr-type="submit" size="large" :block="true" type="primary" :loading="isLoading">
{{ t('category.create.buttonTitle') }}
{{ t('categories.create.buttonTitle') }}
</n-button>
</form>
</n-form>
</template>

<style scoped lang='scss'></style>
20 changes: 18 additions & 2 deletions src/mocks/handlers/category.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { rest } from 'msw'
import _ from 'lodash'
import { faker } from '@faker-js/faker'
import { CreatePagedResponse } from '../handlers.utility'
import type { Category } from '~/models/Category'
import type { Category, CategoryCreateModel } from '~/models/Category'

const categories = _.times(77, createFakeCategory)
const categories = _.times(7, createFakeCategory)
const handlers = [
rest.get('/api/Category', (req, res, ctx) => {
const response = CreatePagedResponse<Category>(req, categories)
Expand All @@ -14,6 +14,22 @@ const handlers = [
ctx.json(response),
)
}),
rest.post('/api/Category', async (req, res, ctx) => {
const newItem = await req.json<CategoryCreateModel>()
const category: Category = {
id: faker.datatype.number({ max: 2000 }),
name: newItem.name,
productsCount: 0,
subItems: [],
}
categories.push(category)
return res(
ctx.status(200),
ctx.delay(200),
ctx.json(category),
)
}),

]

function createFakeCategory(): Category {
Expand Down
5 changes: 5 additions & 0 deletions src/models/Category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ export interface Category {
productsCount: number
subItems?: Category[]
}

export interface CategoryCreateModel {
name: string
parentId: number
}
Loading

0 comments on commit 3b1b82b

Please sign in to comment.