Skip to content

Commit

Permalink
Merge pull request #32 from doroudi:doroudi/issue31
Browse files Browse the repository at this point in the history
Implement colors management section
  • Loading branch information
doroudi authored Nov 20, 2023
2 parents d32913f + 15d5e62 commit 6dce807
Show file tree
Hide file tree
Showing 14 changed files with 405 additions and 8 deletions.
12 changes: 12 additions & 0 deletions locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,16 @@ brands:
url: Url is required
image: Image is required

colors:
title: Colors
createButton: Create
create:
buttonTitle: Create
title: Create New Color
color: Color
name: Name
validations:
nameRequired: Name is required
colorRequired: Color not selected

not-found: Not found
3 changes: 3 additions & 0 deletions src/auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ declare global {
const useClipboard: typeof import('@vueuse/core')['useClipboard']
const useCloned: typeof import('@vueuse/core')['useCloned']
const useColorMode: typeof import('@vueuse/core')['useColorMode']
const useColorStore: typeof import('./store/color.store')['useColorStore']
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
const useCounter: typeof import('@vueuse/core')['useCounter']
const useCssModule: typeof import('vue')['useCssModule']
Expand Down Expand Up @@ -454,6 +455,7 @@ declare module 'vue' {
readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
readonly useColorStore: UnwrapRef<typeof import('./store/color.store')['useColorStore']>
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
Expand Down Expand Up @@ -758,6 +760,7 @@ declare module '@vue/runtime-core' {
readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
readonly useColorStore: UnwrapRef<typeof import('./store/color.store')['useColorStore']>
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
Expand Down
3 changes: 3 additions & 0 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ declare module 'vue' {
Card: typeof import('./components/Card.vue')['default']
CategoryManagement: typeof import('./components/Category/CategoryManagement.vue')['default']
CategoryStatics: typeof import('./components/Category/CategoryStatics.vue')['default']
ColorManagement: typeof import('./components/Color/ColorManagement.vue')['default']
CreateBrand: typeof import('./components/Brand/CreateBrand.vue')['default']
CreateCategory: typeof import('./components/Category/CreateCategory.vue')['default']
CreateColor: typeof import('./components/Color/CreateColor.vue')['default']
DashboardCard: typeof import('./components/DashboardCard.vue')['default']
DonutChart: typeof import('./components/DonutChart.vue')['default']
LanguageSelect: typeof import('./components/LanguageSelect.vue')['default']
LineChart: typeof import('./components/LineChart.vue')['default']
Navbar: typeof import('./components/Navbar.vue')['default']
NBadge: typeof import('naive-ui')['NBadge']
NButton: typeof import('naive-ui')['NButton']
NColorPicker: typeof import('naive-ui')['NColorPicker']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDataTable: typeof import('naive-ui')['NDataTable']
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
Expand Down
11 changes: 6 additions & 5 deletions src/components/Brand/CreateBrand.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<script setup lang='ts'>
import type { FormInst, FormRules } from 'naive-ui/es/form'
import type { UploadFileInfo } from 'naive-ui/es/upload'
import { storeToRefs } from 'pinia'
import type { CategoryCreateModel } from '~/models/Category'
import type { BrandCreateModel } from '~/models/Brand'
const emits = defineEmits(['close'])
const brandStore = useBrandStore()
const { isLoading } = storeToRefs(brandStore)
const brandItem = ref<CategoryCreateModel>({ name: '', parentId: 0 })
const brandItem = ref<BrandCreateModel>({ name: '', url: '', image: '' })
const { t } = useI18n()
const formRef = ref<FormInst | null>(null)
async function create() {
Expand Down Expand Up @@ -46,7 +47,8 @@ const rules: FormRules = {
},
],
}
const previewImageUrl = ref('')
const showModal = ref(false)
const showModalRef = ref(false)
const previewImageUrlRef = ref('')
function handlePreview(file: UploadFileInfo) {
Expand Down Expand Up @@ -78,10 +80,9 @@ function handlePreview(file: UploadFileInfo) {
<div class="form-control">
<n-form-item class="mb-5" path="image" :label="t('brands.create.image')">
<n-upload
:default-file-list="previewFileList"
list-type="image-card"
accept="image/png, image/jpeg"
max="1"
:max="1"
@preview="handlePreview"
/>
<n-modal
Expand Down
151 changes: 151 additions & 0 deletions src/components/Color/ColorManagement.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<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'
import { useDialog, useMessage } from 'naive-ui'
const { t } = useI18n()
const store = useColorStore()
const { colors, isLoading } = storeToRefs(store)
const dialog = useDialog()
const message = useMessage()
onMounted(getItems)
const columns: DataTableColumns<RowData> = [
{
title: 'Color',
key: 'color',
width: 250,
render(row) {
return h(
'span',
{
style: { 'background-color': row.color },
class: 'color-preview',
},
)
},
},
{
title: 'Name',
key: 'name',
},
{
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: () => handleDeleteItem(row),
},
{ default: () => 'Delete' },
),
]
},
},
]
const { options } = storeToRefs(store)
const showAddDialog = ref(false)
function renderIcon(icon: any) {
return () => h(NIcon, null, { default: () => h(icon) })
}
function handleDeleteItem(row: RowData) {
dialog.error({
title: 'Confirm',
content: 'Are you sure?',
positiveText: 'Yes, Delete',
negativeText: 'Cancel',
onPositiveClick: () => {
store.deleteBrand(row.id)
message.success('Brand was deleted!')
},
})
}
function rowKey(row: RowData) {
return row.id
}
function getItems() {
store.getColors(options.value)
}
function handlePageChange(page: number) {
options.value.page = page
getItems()
}
function handleFiltersChange() {
getItems()
}
function createColor() {
showAddDialog.value = true
}
</script>

<template>
<n-layout>
<n-layout-content>
<div>
<div class="flex items-center mb-5">
<h1 class="page-title mx-2">
{{ t('colors.title') }}
</h1>
<NButton type="primary" quaternary round @click="createColor">
<template #icon>
<NIcon>
<PlusIcon />
</NIcon>
</template>
{{ t('brands.createButton') }}
</NButton>
</div>
<n-data-table
remote :columns="columns" :data="colors" :loading="isLoading" :pagination="options"
:row-key="rowKey" @update:sorter="handleSorterChange" @update:filters="handleFiltersChange"
@update:page="handlePageChange"
/>
</div>
</n-layout-content>

<n-drawer v-model:show="showAddDialog" :width="502" placement="right">
<n-drawer-content closable :title="t('colors.create.title')">
<CreateColor @close="showAddDialog = false" />
</n-drawer-content>
</n-drawer>
</n-layout>
</template>

<style lang='scss'>
.color-preview {
display: inline-block;
width: 2rem;
height: 2rem;
border: solid 3px #FFF;
box-shadow: 0 0 3px 0 #989898;
border-radius: 50%;
}
</style>
75 changes: 75 additions & 0 deletions src/components/Color/CreateColor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<script setup lang='ts'>
import type { FormInst, FormRules } from 'naive-ui/es/form'
import { storeToRefs } from 'pinia'
const emits = defineEmits(['close'])
const colorStore = useColorStore()
const { isLoading } = storeToRefs(colorStore)
const colorItem = ref<ColorCreateModel>({ name: '', color: '#000000' })
const { t } = useI18n()
const formRef = ref<FormInst | null>(null)
async function create() {
formRef.value?.validate(async (errors: any) => {
if (!errors) {
await colorStore.createColor(colorItem.value)
emits('close')
}
})
}
const nameInput = ref()
onMounted(() => {
nameInput.value?.focus()
})
const rules: FormRules = {
name: [
{
required: true,
trigger: ['blur', 'change'],
message: t('colors.validations.nameRequired'),
},
],
// color: [
// {
// required: true,
// trigger: ['blur', 'change'],
// message: t('colors.validations.colorRequired'),
// },
// ],
}
</script>

<template>
<n-form ref="formRef" :model="colorItem" :rules="rules" @submit.prevent="create()">
<div class="form-control">
<n-form-item class="mb-5" path="name" :label="t('colors.create.name')">
<n-input
id="name" ref="nameInput" v-model:value="colorItem.name" autofocus
:placeholder="t('brands.create.brandName')"
/>
</n-form-item>
</div>
<div class="form-control flex flex-col mb-5">
<n-form-item :label="t('colors.create.color')">
<n-color-picker
v-model:value="colorItem.color"
:modes="['hex']" :show-alpha="false"
:swatches="[
'#FFFFFF',
'#18A058',
'#2080F0',
'#F0A020',
'rgba(208, 48, 80, 1)',
]"
/>
</n-form-item>
</div>

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

<style scoped lang='scss'></style>
52 changes: 52 additions & 0 deletions src/mocks/handlers/color.handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { rest } from 'msw'
import _ from 'lodash'
import { faker } from '@faker-js/faker'
import { CreatePagedResponse } from '../handlers.utility'
import type { Color, ColorCreateModel } from '~/models/Color'

const colors = _.times(7, createFakeColor)
const handlers = [
rest.get('/api/Color', (req, res, ctx) => {
const response = CreatePagedResponse<Color>(req, colors)
return res(
ctx.status(200),
ctx.delay(200),
ctx.json(response),
)
}),
rest.post('/api/color', async (req, res, ctx) => {
const newItem = await req.json<ColorCreateModel>()
const color: Color = {
id: faker.datatype.number({ max: 2000 }).toString(),
name: newItem.name,
color: newItem.color,
}
colors.push(color)
return res(
ctx.status(200),
ctx.delay(200),
ctx.json(color),
)
}),
rest.delete('/api/Color/:id', (req, res, ctx) => {
const id = req.params.id.toString()
const itemIndex = colors.findIndex(x => x.id === id)
colors.splice(itemIndex, 1)
return res(
ctx.delay(1000),
ctx.status(200),
ctx.json(true),
)
}),

]

function createFakeColor(): Color {
return {
id: faker.datatype.number().toString(),
name: faker.color.human(),
color: faker.color.rgb(),
}
}

export default handlers
6 changes: 5 additions & 1 deletion src/models/Brand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ export interface Brand {
url: string
}

export interface BrandCreateModel extends Brand {}
export interface BrandCreateModel {
name: string
image?: string
url: string
}
10 changes: 10 additions & 0 deletions src/models/Color.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface Color {
id: string
name: string
color: string
}

export interface ColorCreateModel {
name: string
color: string
}
Loading

0 comments on commit 6dce807

Please sign in to comment.