2
0
mirror of https://github.com/frappe/books.git synced 2024-11-10 07:40:55 +00:00

Initialize new theme and pages from scratch

- Add ListView and FormView
This commit is contained in:
Faris Ansari 2018-10-11 00:21:03 +05:30
parent 91f6c693e0
commit fa12e0f1d6
14 changed files with 352 additions and 54 deletions

View File

@ -151,5 +151,25 @@ module.exports = {
<div class='col-4 text-muted'>${data.customer}</div> <div class='col-4 text-muted'>${data.customer}</div>
<div class='col-4 text-muted text-right'>${frappe.format(data.grandTotal, 'Currency')}</div>`; <div class='col-4 text-muted text-right'>${frappe.format(data.grandTotal, 'Currency')}</div>`;
} }
},
listView: {
columns: [
'customer',
{
label: 'Status',
getValue(doc) {
return doc.submitted ? 'Paid' : 'Pending';
}
},
'grandTotal',
'date',
{
label: 'INV #',
getValue(doc) {
return doc.name;
}
}
]
} }
}; };

View File

@ -1,46 +1,24 @@
<template> <template>
<div id="app"> <div id="app">
<frappe-desk v-if="showDesk" :sidebarConfig="sidebarConfig"> <div class="row no-gutters">
<sidebar class="col-2" />
<div class="page-container col-10 bg-light">
<router-view /> <router-view />
</frappe-desk> </div>
<router-view v-else name="setup" /> </div>
</div> </div>
</template> </template>
<script> <script>
import Vue from 'vue'; import Sidebar from './components/Sidebar';
import Observable from 'frappejs/utils/observable';
import Desk from 'frappejs/ui/components/Desk';
import sidebarConfig from './sidebarConfig';
export default { export default {
name: 'App', name: 'App',
data() {
return {
showDesk: true,
sidebarConfig
}
},
components: { components: {
FrappeDesk: Desk, Sidebar
},
async beforeRouteUpdate(to, from, next) {
const accountingSettings = await frappe.getSingle('AccountingSettings');
if (accountingSettings.companyName) {
this.showDesk = true;
} else {
this.showDesk = true;
}
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~bootstrap/scss/bootstrap"; @import "styles/index.scss";
@import '~frappe-datatable/dist/frappe-datatable';
html {
font-size: 14px;
}
</style> </style>

View File

@ -0,0 +1,10 @@
<template>
<div class="page-header px-4 py-3 border-bottom bg-white">
<h5 class="m-0">{{ title }}</h5>
</div>
</template>
<script>
export default {
props: ['title']
}
</script>

View File

@ -0,0 +1,3 @@
<template>
<input type="search" class="form-control" placeholder="Search..." autocomplete="off" spellcheck="false">
</template>

View File

@ -0,0 +1,70 @@
<template>
<div class="page-sidebar bg-dark p-2 text-light">
<div class="company-name px-3 py-2 my-2">
<h6 class="m-0">{{ companyName }}</h6>
</div>
<div :class="['sidebar-item px-3 py-2 ', isCurrentRoute(item.route) ? 'active' : '']" @click="routeTo(item.route)" v-for="item in items" :key="item.label">
{{ item.label }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
companyName: '',
items: [
{
label: 'Invoices',
route: '/list/Invoice'
},
{
label: 'Customers',
route: '/list/Party'
},
{
label: 'Items',
route: '/list/Item'
},
{
label: 'Reports',
route: '/reports'
},
{
label: 'Settings',
route: '/settings'
},
]
}
},
async mounted() {
const accountingSettings = await frappe.getDoc('AccountingSettings');
this.companyName = accountingSettings.companyName;
},
methods: {
isCurrentRoute(route) {
return this.$route.path === route;
},
routeTo(route) {
this.$router.push(route);
}
}
}
</script>
<style lang="scss">
@import "../styles/variables.scss";
.page-sidebar {
height: 100vh;
}
.sidebar-item {
cursor: pointer;
border-radius: $border-radius;
&:hover, &.active {
background-color: $primary;
}
}
</style>

View File

@ -0,0 +1,50 @@
<template>
<div class="bg-light">
<page-header :title="name" />
<div class="form-container col-8 bg-white mt-4 ml-auto mr-auto border px-4 py-3">
<form-layout
class="p-3"
v-if="shouldRenderForm"
:doc="doc"
:fields="meta.fields"
:layout="meta.layout"
:invalid="invalid"
/>
</div>
</div>
</template>
<script>
import frappe from 'frappejs';
import FormLayout from 'frappejs/ui/components/Form/FormLayout';
import PageHeader from '@/components/PageHeader';
export default {
name: 'FormView',
props: ['doctype', 'name'],
components: {
PageHeader,
FormLayout
},
data() {
return {
doc: null
}
},
computed: {
shouldRenderForm() {
return this.name && this.doc;
},
meta() {
return frappe.getMeta(this.doctype);
}
},
async mounted() {
this.doc = await frappe.getDoc(this.doctype, this.name);
}
}
</script>
<style>
.form-container {
}
</style>

View File

@ -0,0 +1,67 @@
<template>
<div class="list-container">
<list-row class="text-muted rounded-top bg-light">
<list-cell v-for="column in columns" :key="column.label">
{{ column.label }}
</list-cell>
</list-row>
<list-row v-for="doc in data" :key="doc.name" @click.native="openForm(doc.name)">
<list-cell v-for="column in columns" :key="column.label">
{{ column.getValue(doc) }}
</list-cell>
</list-row>
</div>
</template>
<script>
import ListRow from './ListRow';
import ListCell from './ListCell';
export default {
name: 'List',
props: ['doctype'],
components: {
ListRow,
ListCell
},
data() {
return {
columns: [],
data: []
};
},
async mounted() {
this.prepareColumns();
this.data = await frappe.db.getAll({
doctype: this.doctype,
fields: ['*']
});
},
methods: {
openForm(name) {
console.log(name);
this.$router.push(`/edit/${this.doctype}/${name}`);
},
prepareColumns() {
this.columns = this.meta.listView.columns.map(col => {
if (typeof col === 'string') {
const field = this.meta.getField(col);
if (!field) return null;
return {
label: field.label,
getValue(doc) {
return doc[col];
}
};
}
return col;
}).filter(Boolean);
}
},
computed: {
meta() {
return frappe.getMeta(this.doctype);
}
}
};
</script>

View File

@ -0,0 +1,10 @@
<template>
<div class="col">
<slot></slot>
</div>
</template>
<script>
export default {
props: ['col']
}
</script>

View File

@ -0,0 +1,21 @@
<template>
<div class="list-row row no-gutters py-2 px-3">
<slot></slot>
</div>
</template>
<style lang="scss">
@import "../../styles/variables";
.list-row {
cursor: pointer;
border: 1px solid $border-color;
background-color: var(--white);
&:hover {
background-color: var(--light);
}
}
.list-row:not(:first-child) {
border-top: none;
}
</style>

View File

@ -0,0 +1,28 @@
<template>
<div class="row">
<div class="col-6 d-flex">
<search-input class="mr-2" />
<f-button secondary>{{ _('Add Filter') }}</f-button>
</div>
<div class="col-6 d-flex flex-row-reverse">
<f-button primary @click="$emit('newClick')">{{ _('New {0}', doctype) }}</f-button>
</div>
</div>
</template>
<script>
import SearchInput from '@/components/SearchInput';
export default {
name: 'ListToolbar',
props: ['doctype'],
components: {
SearchInput
},
methods: {
async newInvoice() {
const doc = await frappe.getNewDoc('Invoice');
this.$formModal.open(doc);
}
}
}
</script>

View File

@ -0,0 +1,42 @@
<template>
<div class="bg-light">
<page-header :title="meta.label || meta.name" />
<div class="px-4 py-3">
<list-toolbar
:doctype="doctype"
@newClick="openNewForm"
class="mb-4"
/>
<list
:doctype="doctype"
/>
</div>
</div>
</template>
<script>
import frappe from 'frappejs';
import PageHeader from '@/components/PageHeader';
import ListToolbar from './ListToolbar';
import List from './List';
export default {
name: 'ListView',
props: ['doctype'],
components: {
PageHeader,
ListToolbar,
List
},
computed: {
meta() {
return frappe.getMeta(this.doctype);
}
},
methods: {
async openNewForm() {
const doc = await frappe.getNewDoc(this.doctype);
this.$router.push(`/edit/${this.doctype}/${doc.name}`);
}
}
}
</script>

View File

@ -1,34 +1,24 @@
import Vue from 'vue'; import Vue from 'vue';
import Router from 'vue-router'; import Router from 'vue-router';
import coreRoutes from 'frappejs/ui/routes';
import SetupWizard from '../pages/SetupWizard'; import ListView from '../pages/ListView';
import Report from 'frappejs/ui/pages/Report'; import FormView from '../pages/FormView';
import reportViewConfig from '../../reports/view';
Vue.use(Router); Vue.use(Router);
const routes = [].concat(coreRoutes, [ const routes = [
{ {
path: '/setup-wizard', path: '/list/:doctype',
name: 'SetupWizard', name: 'ListView',
components: { component: ListView,
setup: SetupWizard props: true
}
}, },
{ {
path: '/report/:reportName', path: '/edit/:doctype/:name',
name: 'Report', name: 'FormView',
component: Report, component: FormView,
props: (route) => { props: true
const { reportName } = route.params;
return {
reportName,
reportConfig: reportViewConfig[reportName] || null,
filters: route.query
};
} }
} ];
]);
export default new Router({ routes }); export default new Router({ routes });

6
src/styles/index.scss Normal file
View File

@ -0,0 +1,6 @@
@import "variables.scss";
@import "~bootstrap/scss/bootstrap";
html {
font-size: 14px;
}

View File

@ -1,2 +1,5 @@
@import "~bootstrap/scss/functions"; @import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables"; @import "~bootstrap/scss/variables";
$dark: #252529;
$blue: #1665D8;