generated from antfu-collective/vitesse
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ✨ as a admin user I want to manage customers
Fixes #47
- Loading branch information
Showing
16 changed files
with
347 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import moment from 'moment' | ||
|
||
const formattedDate = function (value: string, format = 'DD, MMM-YYYY, HH:mm') { | ||
if (value === null) | ||
return value | ||
moment.locale('en') | ||
return moment.utc(value).local().format(format) | ||
} | ||
const shortDate = function (value: string) { | ||
moment.locale('en') | ||
return moment.utc(value).local().format('YYYY/MM') | ||
} | ||
const friendlyTime = function (value: string) { | ||
if (value === null) | ||
return value | ||
return moment(value).local().fromNow() | ||
} | ||
|
||
export default { formattedDate, shortDate, friendlyTime } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
<script setup lang='ts'> | ||
import { getCurrentInstance } from 'vue' | ||
import { type DataTableColumns, NButton, NIcon, NSpace, NText } from 'naive-ui/es/components' | ||
import type { RowData } from 'naive-ui/es/data-table/src/interface' | ||
import { | ||
Delete24Regular as DeleteIcon, | ||
Edit24Regular as EditIcon, | ||
Add24Filled as PlusIcon, | ||
} from '@vicons/fluent' | ||
import { storeToRefs } from 'pinia' | ||
import { useDialog, useMessage } from 'naive-ui' | ||
const { t } = useI18n() | ||
const store = useCustomerStore() | ||
const { customers, isLoading } = storeToRefs(store) | ||
const dialog = useDialog() | ||
const message = useMessage() | ||
const router = useRouter() | ||
const { proxy } = getCurrentInstance() | ||
onMounted(getItems) | ||
const columns: DataTableColumns<RowData> = [ | ||
{ | ||
type: 'selection', | ||
}, | ||
{ | ||
title: 'NAME', | ||
key: 'name', | ||
render: row => | ||
h(NSpace, {}, { | ||
default: () => [ | ||
h(NText, {}, { default: () => `${row.firstName} ${row.lastName}` }), | ||
], | ||
}), | ||
}, | ||
{ | ||
title: 'Join Date', | ||
key: 'join-date', | ||
render(row) { | ||
return h(NText, | ||
{}, { | ||
default: () => proxy.$filters.friendlyTime(row.joinDate), | ||
}) | ||
}, | ||
}, | ||
{ | ||
title: 'Phone', | ||
key: 'phone', | ||
render(row) { | ||
return [ | ||
h(NText, {}, { default: () => row.mobile }), | ||
] | ||
}, | ||
}, | ||
{ | ||
title: 'Email', | ||
key: 'email', | ||
render(row) { | ||
return h(NText, | ||
{}, { | ||
default: () => row.email, | ||
}) | ||
}, | ||
}, | ||
{ | ||
title: 'Orders Count', | ||
key: 'ordersCount', | ||
}, | ||
{ | ||
title: 'Actions', | ||
key: 'actions', | ||
width: 110, | ||
render(row) { | ||
return [ | ||
h( | ||
NButton, | ||
{ | ||
size: 'medium', | ||
renderIcon: renderIcon(EditIcon), | ||
quaternary: true, | ||
circle: true, | ||
class: 'mr-2', | ||
onClick: () => { }, | ||
}, | ||
), | ||
h( | ||
NButton, | ||
{ | ||
size: 'medium', | ||
quaternary: true, | ||
circle: true, | ||
renderIcon: renderIcon(DeleteIcon), | ||
onClick: () => handleDeleteItem(row), | ||
}, | ||
), | ||
] | ||
}, | ||
}, | ||
] | ||
const { options } = storeToRefs(store) | ||
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.deleteProduct(row.id) | ||
message.success('Product was deleted!') | ||
}, | ||
}) | ||
} | ||
function rowKey(row: RowData) { | ||
return row.id | ||
} | ||
function getItems() { | ||
store.getCustomers(options.value) | ||
} | ||
function handlePageChange(page: number) { | ||
options.value.page = page | ||
getItems() | ||
} | ||
function handleSorterChange() { | ||
getItems() | ||
} | ||
function handleFiltersChange() { | ||
getItems() | ||
} | ||
</script> | ||
|
||
<template> | ||
<n-layout> | ||
<n-layout-content> | ||
<div class="px-3"> | ||
<NSpace justify="space-between" class="mb-3"> | ||
<n-input placeholder="Search" /> | ||
<NButton type="primary" @click="router.push('/Products/Create')"> | ||
<template #icon> | ||
<NIcon> | ||
<PlusIcon /> | ||
</NIcon> | ||
</template> | ||
{{ t('categories.createButton') }} | ||
</NButton> | ||
</NSpace> | ||
<n-data-table | ||
remote :columns="columns" :data="customers" :loading="isLoading" :pagination="options" | ||
selectable :row-key="rowKey" @update:sorter="handleSorterChange" @update:filters="handleFiltersChange" | ||
@update:page="handlePageChange" | ||
/> | ||
</div> | ||
</n-layout-content> | ||
</n-layout> | ||
</template> | ||
|
||
<style scoped lang='scss'></style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
src/mocks/handlers/account.handlers.ts → src/mocks/handlers/account.handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { HttpResponse, http } from 'msw' | ||
import _ from 'lodash' | ||
import { faker } from '@faker-js/faker' | ||
import { CreatePagedResponse } from '../handlers.utility' | ||
|
||
import type { Customer, CustomerCreateModel } from '~/models/Customer' | ||
|
||
const customers = _.times(65, createFakeCustomer) | ||
const handlers = [ | ||
http.get('/api/customer', ({ request }) => { | ||
const response = CreatePagedResponse<Customer>(request, customers) | ||
return HttpResponse.json(response, { status: 200 }) | ||
}), | ||
http.post('/api/customer', async ({ request }) => { | ||
const newItem = await request.json() as CustomerCreateModel | ||
const customer: CustomerCreateModel = { | ||
id: faker.number.int({ max: 2000 }).toString(), | ||
firstName: newItem.firstName, | ||
lastName: newItem.lastName, | ||
address: [], | ||
mobile: newItem.mobile, | ||
joinDate: new Date(), | ||
birthDate: newItem.birthDate, | ||
email: newItem.email, | ||
} | ||
customers.push(customer) | ||
return HttpResponse.json(customer, { status: 201 }) | ||
}), | ||
http.delete('/api/customer/:id', ({ params }) => { | ||
const { id } = params | ||
const itemIndex = customers.findIndex(x => x.id === id) | ||
customers.splice(itemIndex, 1) | ||
return HttpResponse.json(true, { status: 200 }) | ||
}), | ||
|
||
] | ||
|
||
function createFakeCustomer(): Customer { | ||
return { | ||
id: faker.number.int().toString(), | ||
firstName: faker.person.firstName(), | ||
lastName: faker.person.lastName(), | ||
address: [], | ||
mobile: faker.phone.number(), | ||
joinDate: faker.date.past(), | ||
birthDate: faker.date.birthdate(), | ||
email: faker.internet.email(), | ||
ordersCount: faker.number.int({ max: 50 }), | ||
} | ||
} | ||
|
||
export default handlers |
Oops, something went wrong.