mirror of
https://github.com/frappe/books.git
synced 2024-12-22 10:58:59 +00:00
feat: windowstitlebar, sidebar and Dashboard changed to darkmode
This commit is contained in:
parent
2119c301d7
commit
e131c560b9
@ -11,6 +11,9 @@
|
|||||||
"600": "#7C7C7C",
|
"600": "#7C7C7C",
|
||||||
"700": "#525252",
|
"700": "#525252",
|
||||||
"800": "#383838",
|
"800": "#383838",
|
||||||
|
"850": "#282828",
|
||||||
|
"875": "#212121",
|
||||||
|
"890": "#1C1C1C",
|
||||||
"900": "#171717"
|
"900": "#171717"
|
||||||
},
|
},
|
||||||
"red": {
|
"red": {
|
||||||
|
@ -36,6 +36,7 @@ export type ConfigMap = {
|
|||||||
lastSelectedFilePath: null | string;
|
lastSelectedFilePath: null | string;
|
||||||
language: string
|
language: string
|
||||||
deviceId: string
|
deviceId: string
|
||||||
|
darkMode: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ConfigFile {
|
export interface ConfigFile {
|
||||||
|
12
src/App.vue
12
src/App.vue
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="app"
|
id="app"
|
||||||
class="h-screen flex flex-col font-sans overflow-hidden antialiased"
|
class="dark:bg-gray-900 h-screen flex flex-col font-sans overflow-hidden antialiased"
|
||||||
:dir="languageDirection"
|
:dir="languageDirection"
|
||||||
:language="language"
|
:language="language"
|
||||||
>
|
>
|
||||||
@ -14,7 +14,9 @@
|
|||||||
<Desk
|
<Desk
|
||||||
v-if="activeScreen === 'Desk'"
|
v-if="activeScreen === 'Desk'"
|
||||||
class="flex-1"
|
class="flex-1"
|
||||||
|
:darkMode="darkMode"
|
||||||
@change-db-file="showDbSelector"
|
@change-db-file="showDbSelector"
|
||||||
|
@toggle-darkmode="toggleDMode"
|
||||||
/>
|
/>
|
||||||
<DatabaseSelector
|
<DatabaseSelector
|
||||||
v-if="activeScreen === 'DatabaseSelector'"
|
v-if="activeScreen === 'DatabaseSelector'"
|
||||||
@ -61,6 +63,7 @@ import { Search } from './utils/search';
|
|||||||
import { Shortcuts } from './utils/shortcuts';
|
import { Shortcuts } from './utils/shortcuts';
|
||||||
import { routeTo } from './utils/ui';
|
import { routeTo } from './utils/ui';
|
||||||
import { useKeys } from './utils/vueUtils';
|
import { useKeys } from './utils/vueUtils';
|
||||||
|
import { toggleDarkMode } from 'src/utils/theme';
|
||||||
|
|
||||||
enum Screen {
|
enum Screen {
|
||||||
Desk = 'Desk',
|
Desk = 'Desk',
|
||||||
@ -106,10 +109,12 @@ export default defineComponent({
|
|||||||
activeScreen: null,
|
activeScreen: null,
|
||||||
dbPath: '',
|
dbPath: '',
|
||||||
companyName: '',
|
companyName: '',
|
||||||
|
darkMode: false,
|
||||||
} as {
|
} as {
|
||||||
activeScreen: null | Screen;
|
activeScreen: null | Screen;
|
||||||
dbPath: string;
|
dbPath: string;
|
||||||
companyName: string;
|
companyName: string;
|
||||||
|
darkMode: boolean | undefined;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -124,6 +129,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
await this.setInitialScreen();
|
await this.setInitialScreen();
|
||||||
|
this.darkMode = fyo.config.get('darkMode') as boolean;
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async setInitialScreen(): Promise<void> {
|
async setInitialScreen(): Promise<void> {
|
||||||
@ -247,6 +253,10 @@ export default defineComponent({
|
|||||||
this.searcher = null;
|
this.searcher = null;
|
||||||
this.companyName = '';
|
this.companyName = '';
|
||||||
},
|
},
|
||||||
|
async toggleDMode(): Promise<void> {
|
||||||
|
toggleDarkMode();
|
||||||
|
this.darkMode = fyo.config.get('darkMode');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -39,10 +39,10 @@ export default defineComponent({
|
|||||||
_class() {
|
_class() {
|
||||||
return {
|
return {
|
||||||
'opacity-50 cursor-not-allowed pointer-events-none': this.disabled,
|
'opacity-50 cursor-not-allowed pointer-events-none': this.disabled,
|
||||||
'text-white': this.type === 'primary',
|
'text-white dark:text-black': this.type === 'primary',
|
||||||
'bg-black': this.type === 'primary' && this.background,
|
'bg-black dark:bg-gray-400': this.type === 'primary' && this.background,
|
||||||
'text-gray-700': this.type !== 'primary',
|
'text-gray-700 dark:text-gray-200': this.type !== 'primary',
|
||||||
'bg-gray-200': this.type !== 'primary' && this.background,
|
'bg-gray-200 dark:bg-gray-900': this.type !== 'primary' && this.background,
|
||||||
'h-8': this.background,
|
'h-8': this.background,
|
||||||
'px-3': this.padding && this.icon,
|
'px-3': this.padding && this.icon,
|
||||||
'px-6': this.padding && !this.icon,
|
'px-6': this.padding && !this.icon,
|
||||||
|
@ -118,7 +118,7 @@
|
|||||||
ref="tooltip"
|
ref="tooltip"
|
||||||
:offset="15"
|
:offset="15"
|
||||||
placement="top"
|
placement="top"
|
||||||
class="text-sm shadow-md px-2 py-1 bg-white text-gray-900 border-s-4"
|
class="text-sm shadow-md px-2 py-1 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-200 border-s-4"
|
||||||
:style="{ borderColor: activeColor }"
|
:style="{ borderColor: activeColor }"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col justify-center items-center">
|
<div class="flex flex-col justify-center items-center">
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
:key="i"
|
:key="i"
|
||||||
clip-path="url(#donut-hole)"
|
clip-path="url(#donut-hole)"
|
||||||
:d="getArcPath(cx, cy, radius, start_, theta)"
|
:d="getArcPath(cx, cy, radius, start_, theta)"
|
||||||
:stroke="sectors[i].color"
|
:stroke="getSectorColor(i)"
|
||||||
:stroke-width="thickness + (active === i ? 4 : 0)"
|
:stroke-width="thickness + (active === i ? 4 : 0)"
|
||||||
:style="{ transformOrigin: `${cx}px ${cy}px` }"
|
:style="{ transformOrigin: `${cx}px ${cy}px` }"
|
||||||
class="sector"
|
class="sector"
|
||||||
@ -57,7 +57,11 @@
|
|||||||
:x="cx"
|
:x="cx"
|
||||||
:y="cy"
|
:y="cy"
|
||||||
text-anchor="middle"
|
text-anchor="middle"
|
||||||
style="font-size: 6px; font-weight: bold; fill: #415668"
|
:style="{
|
||||||
|
fontSize: '6px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: darkMode ? '#FFFFFF' : '#415668',
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
valueFormatter(
|
valueFormatter(
|
||||||
@ -101,6 +105,7 @@ export default {
|
|||||||
offsetY: { default: 0, type: Number },
|
offsetY: { default: 0, type: Number },
|
||||||
textOffsetX: { default: 0, type: Number },
|
textOffsetX: { default: 0, type: Number },
|
||||||
textOffsetY: { default: 0, type: Number },
|
textOffsetY: { default: 0, type: Number },
|
||||||
|
darkMode: Boolean,
|
||||||
},
|
},
|
||||||
emits: ['change'],
|
emits: ['change'],
|
||||||
computed: {
|
computed: {
|
||||||
@ -150,6 +155,13 @@ export default {
|
|||||||
|
|
||||||
return `M ${startX} ${startY} A ${r} ${r} 0 ${largeArcFlag} ${sweepFlag} ${endX} ${endY}`;
|
return `M ${startX} ${startY} A ${r} ${r} 0 ${largeArcFlag} ${sweepFlag} ${endX} ${endY}`;
|
||||||
},
|
},
|
||||||
|
getSectorColor(index) {
|
||||||
|
if (this.darkMode) {
|
||||||
|
return this.sectors[index].color.darkColor;
|
||||||
|
} else {
|
||||||
|
return this.sectors[index].color.color;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -117,7 +117,7 @@
|
|||||||
ref="tooltip"
|
ref="tooltip"
|
||||||
:offset="15"
|
:offset="15"
|
||||||
placement="top"
|
placement="top"
|
||||||
class="text-sm shadow-md px-2 py-1 bg-white text-gray-900 border-s-4"
|
class="text-sm shadow-md px-2 py-1 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-200 border-s-4"
|
||||||
:style="{ borderColor: colors[yi] }"
|
:style="{ borderColor: colors[yi] }"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col justify-center items-center">
|
<div class="flex flex-col justify-center items-center">
|
||||||
|
@ -15,14 +15,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="bg-white rounded w-full min-w-40 overflow-hidden">
|
<div
|
||||||
|
class="bg-white dark:bg-gray-850 dark:text-white rounded w-full min-w-40 overflow-hidden"
|
||||||
|
>
|
||||||
<div class="p-1 max-h-64 overflow-auto custom-scroll text-sm">
|
<div class="p-1 max-h-64 overflow-auto custom-scroll text-sm">
|
||||||
<div v-if="isLoading" class="p-2 text-gray-600 italic">
|
<div
|
||||||
|
v-if="isLoading"
|
||||||
|
class="p-2 text-gray-600 dark:text-gray-400 italic"
|
||||||
|
>
|
||||||
{{ t`Loading...` }}
|
{{ t`Loading...` }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else-if="dropdownItems.length === 0"
|
v-else-if="dropdownItems.length === 0"
|
||||||
class="p-2 text-gray-600 italic"
|
class="p-2 text-gray-600 dark:text-gray-400 italic"
|
||||||
>
|
>
|
||||||
{{ getEmptyMessage() }}
|
{{ getEmptyMessage() }}
|
||||||
</div>
|
</div>
|
||||||
@ -34,31 +39,18 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="d.isGroup"
|
v-if="d.isGroup"
|
||||||
class="
|
class="px-2 pt-3 pb-1 text-xs uppercase text-gray-700 dark:text-gray-400 font-semibold tracking-wider"
|
||||||
px-2
|
|
||||||
pt-3
|
|
||||||
pb-1
|
|
||||||
text-xs
|
|
||||||
uppercase
|
|
||||||
text-gray-700
|
|
||||||
font-semibold
|
|
||||||
tracking-wider
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{{ d.label }}
|
{{ d.label }}
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
v-else
|
v-else
|
||||||
class="
|
class="block p-2 rounded-md mt-1 first:mt-0 cursor-pointer truncate"
|
||||||
block
|
:class="
|
||||||
p-2
|
index === highlightedIndex
|
||||||
rounded-md
|
? 'bg-gray-100 dark:bg-gray-875'
|
||||||
mt-1
|
: ''
|
||||||
first:mt-0
|
|
||||||
cursor-pointer
|
|
||||||
truncate
|
|
||||||
"
|
"
|
||||||
:class="index === highlightedIndex ? 'bg-gray-100' : ''"
|
|
||||||
@mouseenter="highlightedIndex = index"
|
@mouseenter="highlightedIndex = index"
|
||||||
@mousedown.prevent
|
@mousedown.prevent
|
||||||
@click="selectItem(d)"
|
@click="selectItem(d)"
|
||||||
|
@ -7,7 +7,11 @@
|
|||||||
<template #target="{ togglePopover }">
|
<template #target="{ togglePopover }">
|
||||||
<Button :icon="true" @click="togglePopover()">
|
<Button :icon="true" @click="togglePopover()">
|
||||||
<span class="flex items-center">
|
<span class="flex items-center">
|
||||||
<Icon name="filter" size="12" class="stroke-current text-gray-700" />
|
<Icon
|
||||||
|
name="filter"
|
||||||
|
size="12"
|
||||||
|
class="stroke-current text-gray-700 dark:text-gray-400"
|
||||||
|
/>
|
||||||
<span class="ms-1">
|
<span class="ms-1">
|
||||||
<template v-if="activeFilterCount > 0">
|
<template v-if="activeFilterCount > 0">
|
||||||
{{ filterAppliedMessage }}
|
{{ filterAppliedMessage }}
|
||||||
@ -30,18 +34,7 @@
|
|||||||
class="flex items-center justify-between text-base gap-2"
|
class="flex items-center justify-between text-base gap-2"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="
|
class="cursor-pointer w-4 h-4 flex items-center justify-center text-gray-600 hover:text-gray-800 rounded-md group"
|
||||||
cursor-pointer
|
|
||||||
w-4
|
|
||||||
h-4
|
|
||||||
flex
|
|
||||||
items-center
|
|
||||||
justify-center
|
|
||||||
text-gray-600
|
|
||||||
hover:text-gray-800
|
|
||||||
rounded-md
|
|
||||||
group
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<span class="hidden group-hover:inline-block">
|
<span class="hidden group-hover:inline-block">
|
||||||
<feather-icon
|
<feather-icon
|
||||||
@ -106,16 +99,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="
|
class="text-base border-t p-2 flex items-center text-gray-600 cursor-pointer hover:bg-gray-100"
|
||||||
text-base
|
|
||||||
border-t
|
|
||||||
p-2
|
|
||||||
flex
|
|
||||||
items-center
|
|
||||||
text-gray-600
|
|
||||||
cursor-pointer
|
|
||||||
hover:bg-gray-100
|
|
||||||
"
|
|
||||||
@click="addNewFilter"
|
@click="addNewFilter"
|
||||||
>
|
>
|
||||||
<feather-icon name="plus" class="w-4 h-4" />
|
<feather-icon name="plus" class="w-4 h-4" />
|
||||||
@ -149,7 +133,7 @@ const conditions = [
|
|||||||
{ label: t`Is Not Empty`, value: 'is not null' },
|
{ label: t`Is Not Empty`, value: 'is not null' },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
type Condition = typeof conditions[number]['value'];
|
type Condition = (typeof conditions)[number]['value'];
|
||||||
|
|
||||||
type Filter = {
|
type Filter = {
|
||||||
fieldname: string;
|
fieldname: string;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
:is="iconComponent"
|
:is="iconComponent"
|
||||||
:class="iconClasses"
|
:class="iconClasses"
|
||||||
:active="active"
|
:active="active"
|
||||||
|
:darkMode="darkMode"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
name: { type: String, required: true },
|
name: { type: String, required: true },
|
||||||
active: { type: Boolean, default: false },
|
active: { type: Boolean, default: false },
|
||||||
|
darkMode: { type: Boolean, default: false },
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -1,15 +1,30 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
import { uicolors } from 'src/utils/colors';
|
import { uicolors } from 'src/utils/colors';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'IconBase',
|
name: 'IconBase',
|
||||||
props: ['active'],
|
props: {
|
||||||
computed: {
|
active: Boolean,
|
||||||
lightColor() {
|
darkMode: Boolean,
|
||||||
return this.active ? uicolors.gray['600'] : uicolors.gray['400'];
|
|
||||||
},
|
},
|
||||||
darkColor() {
|
computed: {
|
||||||
return this.active ? uicolors.gray['800'] : uicolors.gray['600'];
|
lightColor(): string {
|
||||||
|
const activeGray = this.darkMode
|
||||||
|
? uicolors.gray['500']
|
||||||
|
: uicolors.gray['600'];
|
||||||
|
const passiveGray = this.darkMode
|
||||||
|
? uicolors.gray['700']
|
||||||
|
: uicolors.gray['400'];
|
||||||
|
return this.active ? activeGray : passiveGray;
|
||||||
|
},
|
||||||
|
darkColor(): string {
|
||||||
|
const activeGray = this.darkMode
|
||||||
|
? uicolors.gray['200']
|
||||||
|
: uicolors.gray['800'];
|
||||||
|
const passiveGray = this.darkMode
|
||||||
|
? uicolors.gray['500']
|
||||||
|
: uicolors.gray['600'];
|
||||||
|
return this.active ? activeGray : passiveGray;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="px-4 flex justify-between items-center h-row-largest flex-shrink-0"
|
class="px-4 flex justify-between items-center h-row-largest flex-shrink-0 dark:bg-gray-875"
|
||||||
:class="[
|
:class="[
|
||||||
border ? 'border-b' : '',
|
border ? 'border-b dark:border-gray-800' : '',
|
||||||
platform !== 'Windows' ? 'window-drag' : '',
|
platform !== 'Windows' ? 'window-drag' : '',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
<PageHeaderNavGroup />
|
<PageHeaderNavGroup />
|
||||||
<h1
|
<h1
|
||||||
v-if="title"
|
v-if="title"
|
||||||
class="text-xl font-semibold select-none whitespace-nowrap"
|
class="text-xl font-semibold select-none whitespace-nowrap dark:text-white"
|
||||||
>
|
>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -4,9 +4,11 @@
|
|||||||
<!-- Back Button -->
|
<!-- Back Button -->
|
||||||
<a
|
<a
|
||||||
ref="backlink"
|
ref="backlink"
|
||||||
class="nav-link border-l border-r border-white"
|
class="nav-link border-l border-r border-white dark:border-gray-850 dark:bg-gray-900"
|
||||||
:class="
|
:class="
|
||||||
historyState.back ? 'text-gray-700 cursor-pointer' : 'text-gray-400'
|
historyState.back
|
||||||
|
? 'text-gray-700 dark:text-gray-300 cursor-pointer'
|
||||||
|
: 'text-gray-400 dark:text-gray-700'
|
||||||
"
|
"
|
||||||
@click="$router.back()"
|
@click="$router.back()"
|
||||||
>
|
>
|
||||||
@ -14,9 +16,11 @@
|
|||||||
</a>
|
</a>
|
||||||
<!-- Forward Button -->
|
<!-- Forward Button -->
|
||||||
<a
|
<a
|
||||||
class="nav-link rounded-md rounded-l-none"
|
class="nav-link rounded-md rounded-l-none dark:bg-gray-900"
|
||||||
:class="
|
:class="
|
||||||
historyState.forward ? 'text-gray-700 cursor-pointer' : 'text-gray-400'
|
historyState.forward
|
||||||
|
? 'text-gray-700 dark:text-gray-400 cursor-pointer'
|
||||||
|
: 'text-gray-400 dark:text-gray-700'
|
||||||
"
|
"
|
||||||
@click="$router.forward()"
|
@click="$router.forward()"
|
||||||
>
|
>
|
||||||
|
@ -12,15 +12,7 @@
|
|||||||
v-show="isOpen"
|
v-show="isOpen"
|
||||||
ref="popover"
|
ref="popover"
|
||||||
:class="popoverClass"
|
:class="popoverClass"
|
||||||
class="
|
class="bg-white dark:bg-gray-850 rounded-md border dark:border-gray-875 shadow-lg popover-container relative z-10"
|
||||||
bg-white
|
|
||||||
rounded-md
|
|
||||||
border
|
|
||||||
shadow-lg
|
|
||||||
popover-container
|
|
||||||
relative
|
|
||||||
z-10
|
|
||||||
"
|
|
||||||
:style="{ 'transition-delay': `${isOpen ? entryDelay : exitDelay}ms` }"
|
:style="{ 'transition-delay': `${isOpen ? entryDelay : exitDelay}ms` }"
|
||||||
>
|
>
|
||||||
<slot name="content" :toggle-popover="togglePopover"></slot>
|
<slot name="content" :toggle-popover="togglePopover"></slot>
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- Search Bar Button -->
|
<!-- Search Bar Button -->
|
||||||
<Button class="px-3 py-2 rounded-r-none" :padding="false" @click="open">
|
<Button
|
||||||
<feather-icon name="search" class="w-4 h-4 text-gray-700" />
|
class="px-3 py-2 rounded-r-none dark:bg-gray-900"
|
||||||
|
:padding="false"
|
||||||
|
@click="open"
|
||||||
|
>
|
||||||
|
<feather-icon
|
||||||
|
name="search"
|
||||||
|
class="w-4 h-4 text-gray-700 dark:text-gray-300"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -22,16 +29,7 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
:placeholder="t`Type to search...`"
|
:placeholder="t`Type to search...`"
|
||||||
class="
|
class="bg-gray-100 text-2xl focus:outline-none w-full placeholder-gray-500 text-gray-900 rounded-md p-3"
|
||||||
bg-gray-100
|
|
||||||
text-2xl
|
|
||||||
focus:outline-none
|
|
||||||
w-full
|
|
||||||
placeholder-gray-500
|
|
||||||
text-gray-900
|
|
||||||
rounded-md
|
|
||||||
p-3
|
|
||||||
"
|
|
||||||
@keydown.up="up"
|
@keydown.up="up"
|
||||||
@keydown.down="down"
|
@keydown.down="down"
|
||||||
@keydown.enter="() => select()"
|
@keydown.enter="() => select()"
|
||||||
@ -129,14 +127,7 @@
|
|||||||
<button
|
<button
|
||||||
v-for="sf in schemaFilters"
|
v-for="sf in schemaFilters"
|
||||||
:key="sf.value"
|
:key="sf.value"
|
||||||
class="
|
class="border px-1 py-0.5 rounded-lg border-blue-100 whitespace-nowrap"
|
||||||
border
|
|
||||||
px-1
|
|
||||||
py-0.5
|
|
||||||
rounded-lg
|
|
||||||
border-blue-100
|
|
||||||
whitespace-nowrap
|
|
||||||
"
|
|
||||||
:class="{
|
:class="{
|
||||||
'bg-blue-100': searcher?.filters.schemaFilters[sf.value],
|
'bg-blue-100': searcher?.filters.schemaFilters[sf.value],
|
||||||
}"
|
}"
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="py-2 h-full flex justify-between flex-col bg-gray-25 relative"
|
class="py-2 h-full flex justify-between flex-col bg-gray-25 dark:bg-gray-900 relative"
|
||||||
:class="{
|
:class="{
|
||||||
'window-drag': platform !== 'Windows',
|
'window-drag': platform !== 'Windows',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<!-- Company name and DB Switcher -->
|
<!-- Company name -->
|
||||||
<div
|
<div
|
||||||
class="px-4 flex flex-row items-center justify-between mb-4"
|
class="px-4 flex flex-row items-center justify-between mb-4"
|
||||||
:class="
|
:class="
|
||||||
@ -15,13 +15,7 @@
|
|||||||
>
|
>
|
||||||
<h6
|
<h6
|
||||||
data-testid="company-name"
|
data-testid="company-name"
|
||||||
class="
|
class="font-semibold dark:text-gray-200 whitespace-nowrap overflow-auto no-scrollbar select-none"
|
||||||
font-semibold
|
|
||||||
whitespace-nowrap
|
|
||||||
overflow-auto
|
|
||||||
no-scrollbar
|
|
||||||
select-none
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{{ companyName }}
|
{{ companyName }}
|
||||||
</h6>
|
</h6>
|
||||||
@ -30,10 +24,10 @@
|
|||||||
<!-- Sidebar Items -->
|
<!-- Sidebar Items -->
|
||||||
<div v-for="group in groups" :key="group.label">
|
<div v-for="group in groups" :key="group.label">
|
||||||
<div
|
<div
|
||||||
class="px-4 flex items-center cursor-pointer hover:bg-gray-100 h-10"
|
class="px-4 flex items-center cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-875 h-10"
|
||||||
:class="
|
:class="
|
||||||
isGroupActive(group) && !group.items
|
isGroupActive(group) && !group.items
|
||||||
? 'bg-gray-100 border-s-4 border-gray-800'
|
? 'bg-gray-100 dark:bg-gray-875 border-s-4 border-gray-800 dark:border-gray-100'
|
||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
@click="routeToSidebarItem(group)"
|
@click="routeToSidebarItem(group)"
|
||||||
@ -44,11 +38,16 @@
|
|||||||
:size="group.iconSize || '18'"
|
:size="group.iconSize || '18'"
|
||||||
:height="group.iconHeight ?? 0"
|
:height="group.iconHeight ?? 0"
|
||||||
:active="!!isGroupActive(group)"
|
:active="!!isGroupActive(group)"
|
||||||
|
:darkMode="darkMode"
|
||||||
:class="isGroupActive(group) && !group.items ? '-ms-1' : ''"
|
:class="isGroupActive(group) && !group.items ? '-ms-1' : ''"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="ms-2 text-lg text-gray-700"
|
class="ms-2 text-lg text-gray-700"
|
||||||
:class="isGroupActive(group) && !group.items && 'text-gray-900'"
|
:class="
|
||||||
|
isGroupActive(group) && !group.items
|
||||||
|
? 'text-gray-900 dark:text-gray-25'
|
||||||
|
: 'dark:text-gray-300'
|
||||||
|
"
|
||||||
>
|
>
|
||||||
{{ group.label }}
|
{{ group.label }}
|
||||||
</div>
|
</div>
|
||||||
@ -59,19 +58,11 @@
|
|||||||
<div
|
<div
|
||||||
v-for="item in group.items"
|
v-for="item in group.items"
|
||||||
:key="item.label"
|
:key="item.label"
|
||||||
class="
|
class="text-base h-10 ps-10 cursor-pointer flex items-center hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||||
text-base
|
|
||||||
h-10
|
|
||||||
ps-10
|
|
||||||
cursor-pointer
|
|
||||||
flex
|
|
||||||
items-center
|
|
||||||
hover:bg-gray-100
|
|
||||||
"
|
|
||||||
:class="
|
:class="
|
||||||
isItemActive(item)
|
isItemActive(item)
|
||||||
? 'bg-gray-100 text-gray-900 border-s-4 border-gray-800'
|
? 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100 border-s-4 border-gray-800 dark:border-gray-100'
|
||||||
: 'text-gray-700'
|
: 'text-gray-700 dark:text-gray-400'
|
||||||
"
|
"
|
||||||
@click="routeToSidebarItem(item)"
|
@click="routeToSidebarItem(item)"
|
||||||
>
|
>
|
||||||
@ -83,16 +74,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Report Issue and App Version -->
|
<!-- Report Issue, DB Switcher and Darkmode Switcher -->
|
||||||
<div class="window-no-drag flex flex-col gap-2 py-2 px-4">
|
<div class="window-no-drag flex flex-col gap-2 py-2 px-4">
|
||||||
<button
|
<button
|
||||||
class="
|
class="flex text-sm text-gray-600 dark:text-gray-500 hover:text-gray-800 dark:hover:text-gray-400 gap-1 items-center"
|
||||||
flex
|
@click="$emit('toggle-darkmode')"
|
||||||
text-sm text-gray-600
|
>
|
||||||
hover:text-gray-800
|
<feather-icon :name="darkMode?'sun':'moon'" class="h-4 w-4 flex-shrink-0" />
|
||||||
gap-1
|
<p>
|
||||||
items-center
|
{{ t`Dark Mode` }}
|
||||||
"
|
</p>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="flex text-sm text-gray-600 dark:text-gray-500 hover:text-gray-800 dark:hover:text-gray-400 gap-1 items-center"
|
||||||
@click="openDocumentation"
|
@click="openDocumentation"
|
||||||
>
|
>
|
||||||
<feather-icon name="help-circle" class="h-4 w-4 flex-shrink-0" />
|
<feather-icon name="help-circle" class="h-4 w-4 flex-shrink-0" />
|
||||||
@ -102,13 +97,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="
|
class="flex text-sm text-gray-600 dark:text-gray-500 hover:text-gray-800 dark:hover:text-gray-400 gap-1 items-center"
|
||||||
flex
|
|
||||||
text-sm text-gray-600
|
|
||||||
hover:text-gray-800
|
|
||||||
gap-1
|
|
||||||
items-center
|
|
||||||
"
|
|
||||||
@click="viewShortcuts = true"
|
@click="viewShortcuts = true"
|
||||||
>
|
>
|
||||||
<feather-icon name="command" class="h-4 w-4 flex-shrink-0" />
|
<feather-icon name="command" class="h-4 w-4 flex-shrink-0" />
|
||||||
@ -117,13 +106,7 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
data-testid="change-db"
|
data-testid="change-db"
|
||||||
class="
|
class="flex text-sm text-gray-600 dark:text-gray-500 hover:text-gray-800 dark:hover:text-gray-400 gap-1 items-center"
|
||||||
flex
|
|
||||||
text-sm text-gray-600
|
|
||||||
hover:text-gray-800
|
|
||||||
gap-1
|
|
||||||
items-center
|
|
||||||
"
|
|
||||||
@click="$emit('change-db-file')"
|
@click="$emit('change-db-file')"
|
||||||
>
|
>
|
||||||
<feather-icon name="database" class="h-4 w-4 flex-shrink-0" />
|
<feather-icon name="database" class="h-4 w-4 flex-shrink-0" />
|
||||||
@ -131,13 +114,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="
|
class="flex text-sm text-gray-600 dark:text-gray-500 hover:text-gray-800 dark:hover:text-gray-400 gap-1 items-center"
|
||||||
flex
|
|
||||||
text-sm text-gray-600
|
|
||||||
hover:text-gray-800
|
|
||||||
gap-1
|
|
||||||
items-center
|
|
||||||
"
|
|
||||||
@click="() => reportIssue()"
|
@click="() => reportIssue()"
|
||||||
>
|
>
|
||||||
<feather-icon name="flag" class="h-4 w-4 flex-shrink-0" />
|
<feather-icon name="flag" class="h-4 w-4 flex-shrink-0" />
|
||||||
@ -158,17 +135,7 @@
|
|||||||
|
|
||||||
<!-- Hide Sidebar Button -->
|
<!-- Hide Sidebar Button -->
|
||||||
<button
|
<button
|
||||||
class="
|
class="absolute bottom-0 end-0 text-gray-600 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-875 rounded p-1 m-4 rtl-rotate-180"
|
||||||
absolute
|
|
||||||
bottom-0
|
|
||||||
end-0
|
|
||||||
text-gray-600
|
|
||||||
hover:bg-gray-100
|
|
||||||
rounded
|
|
||||||
p-1
|
|
||||||
m-4
|
|
||||||
rtl-rotate-180
|
|
||||||
"
|
|
||||||
@click="() => toggleSidebar()"
|
@click="() => toggleSidebar()"
|
||||||
>
|
>
|
||||||
<feather-icon name="chevrons-left" class="w-4 h-4" />
|
<feather-icon name="chevrons-left" class="w-4 h-4" />
|
||||||
@ -201,7 +168,10 @@ export default defineComponent({
|
|||||||
Modal,
|
Modal,
|
||||||
ShortcutsHelper,
|
ShortcutsHelper,
|
||||||
},
|
},
|
||||||
emits: ['change-db-file'],
|
props: {
|
||||||
|
darkMode: Boolean,
|
||||||
|
},
|
||||||
|
emits: ['change-db-file', 'toggle-darkmode'],
|
||||||
setup() {
|
setup() {
|
||||||
return {
|
return {
|
||||||
languageDirection: inject(languageDirectionKey),
|
languageDirection: inject(languageDirectionKey),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="window-drag flex items-center border-b text-gray-900 border-gray-100"
|
class="window-drag flex items-center border-b dark:bg-gray-900 text-gray-900 dark:text-gray-100 border-gray-100 dark:border-gray-800"
|
||||||
style="height: 28px"
|
style="height: 28px"
|
||||||
>
|
>
|
||||||
<Fb class="ms-2" />
|
<Fb class="ms-2" />
|
||||||
|
@ -2,20 +2,26 @@
|
|||||||
<div>
|
<div>
|
||||||
<!-- Title and Period Selector -->
|
<!-- Title and Period Selector -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="font-semibold text-base">{{ t`Cashflow` }}</div>
|
<div class="font-semibold text-base dark:text-white">
|
||||||
|
{{ t`Cashflow` }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Chart Legend -->
|
<!-- Chart Legend -->
|
||||||
<div v-if="hasData" class="flex text-base gap-8">
|
<div v-if="hasData" class="flex text-base gap-8">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="w-3 h-3 rounded-sm inline-block bg-blue-500" />
|
<span
|
||||||
<span class="text-gray-900">{{ t`Inflow` }}</span>
|
class="w-3 h-3 rounded-sm inline-block bg-blue-500 dark:bg-blue-600"
|
||||||
|
/>
|
||||||
|
<span class="text-gray-900 dark:text-gray-25">{{ t`Inflow` }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="w-3 h-3 rounded-sm inline-block bg-pink-500" />
|
<span
|
||||||
<span class="text-gray-900">{{ t`Outflow` }}</span>
|
class="w-3 h-3 rounded-sm inline-block bg-pink-500 dark:bg-pink-600"
|
||||||
|
/>
|
||||||
|
<span class="text-gray-900 dark:text-gray-25">{{ t`Outflow` }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="w-16 h-5 bg-gray-200 rounded" />
|
<div v-else class="w-16 h-5 bg-gray-200 dark:bg-gray-700 rounded" />
|
||||||
|
|
||||||
<PeriodSelector
|
<PeriodSelector
|
||||||
v-if="hasData"
|
v-if="hasData"
|
||||||
@ -23,7 +29,7 @@
|
|||||||
:options="periodOptions"
|
:options="periodOptions"
|
||||||
@change="(value) => (period = value)"
|
@change="(value) => (period = value)"
|
||||||
/>
|
/>
|
||||||
<div v-else class="w-20 h-5 bg-gray-200 rounded" />
|
<div v-else class="w-20 h-5 bg-gray-200 dark:bg-gray-700 rounded" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Line Chart -->
|
<!-- Line Chart -->
|
||||||
@ -32,6 +38,8 @@
|
|||||||
class="mt-4"
|
class="mt-4"
|
||||||
:aspect-ratio="4.15"
|
:aspect-ratio="4.15"
|
||||||
:colors="chartData.colors"
|
:colors="chartData.colors"
|
||||||
|
:gridColor="chartData.gridColor"
|
||||||
|
:fontColor="chartData.fontColor"
|
||||||
:points="chartData.points"
|
:points="chartData.points"
|
||||||
:x-labels="chartData.xLabels"
|
:x-labels="chartData.xLabels"
|
||||||
:format="chartData.format"
|
:format="chartData.format"
|
||||||
@ -68,6 +76,9 @@ export default defineComponent({
|
|||||||
PeriodSelector,
|
PeriodSelector,
|
||||||
LineChart,
|
LineChart,
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
darkMode: Boolean,
|
||||||
|
},
|
||||||
extends: DashboardChartBase,
|
extends: DashboardChartBase,
|
||||||
data: () => ({
|
data: () => ({
|
||||||
data: [] as { inflow: number; outflow: number; yearmonth: string }[],
|
data: [] as { inflow: number; outflow: number; yearmonth: string }[],
|
||||||
@ -78,10 +89,16 @@ export default defineComponent({
|
|||||||
computed: {
|
computed: {
|
||||||
chartData() {
|
chartData() {
|
||||||
let data = this.data;
|
let data = this.data;
|
||||||
let colors = [uicolors.blue['500'], uicolors.pink['500']];
|
let colors = [
|
||||||
|
uicolors.blue[this.darkMode ? '600' : '500'],
|
||||||
|
uicolors.pink[this.darkMode ? '600' : '500'],
|
||||||
|
];
|
||||||
if (!this.hasData) {
|
if (!this.hasData) {
|
||||||
data = dummyData;
|
data = dummyData;
|
||||||
colors = [uicolors.gray['200'], uicolors.gray['100']];
|
colors = [
|
||||||
|
this.darkMode ? uicolors.gray['700'] : uicolors.gray['200'],
|
||||||
|
this.darkMode ? uicolors.gray['800'] : uicolors.gray['100'],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
const xLabels = data.map((cf) => cf.yearmonth);
|
const xLabels = data.map((cf) => cf.yearmonth);
|
||||||
@ -91,7 +108,16 @@ export default defineComponent({
|
|||||||
|
|
||||||
const format = (value: number) => fyo.format(value ?? 0, 'Currency');
|
const format = (value: number) => fyo.format(value ?? 0, 'Currency');
|
||||||
const yMax = getYMax(points);
|
const yMax = getYMax(points);
|
||||||
return { points, xLabels, colors, format, yMax, formatX: formatXLabels };
|
return {
|
||||||
|
points,
|
||||||
|
xLabels,
|
||||||
|
colors,
|
||||||
|
format,
|
||||||
|
yMax,
|
||||||
|
formatX: formatXLabels,
|
||||||
|
gridColor: this.darkMode ? 'rgba(200, 200, 200, 0.2)' : undefined,
|
||||||
|
fontColor: this.darkMode ? uicolors.gray['400'] : undefined,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async activated() {
|
async activated() {
|
||||||
|
@ -2,14 +2,7 @@
|
|||||||
<div class="h-screen" style="width: var(--w-desk)">
|
<div class="h-screen" style="width: var(--w-desk)">
|
||||||
<PageHeader :title="t`Dashboard`">
|
<PageHeader :title="t`Dashboard`">
|
||||||
<div
|
<div
|
||||||
class="
|
class="border dark:border-gray-900 rounded bg-gray-50 dark:bg-gray-890 focus-within:bg-gray-100 dark:focus-within:bg-gray-900 flex items-center"
|
||||||
border
|
|
||||||
rounded
|
|
||||||
bg-gray-50
|
|
||||||
focus-within:bg-gray-100
|
|
||||||
flex
|
|
||||||
items-center
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<PeriodSelector
|
<PeriodSelector
|
||||||
class="px-3"
|
class="px-3"
|
||||||
@ -21,43 +14,48 @@
|
|||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="no-scrollbar overflow-auto"
|
class="no-scrollbar overflow-auto dark:bg-gray-875"
|
||||||
style="height: calc(100vh - var(--h-row-largest) - 1px)"
|
style="height: calc(100vh - var(--h-row-largest) - 1px)"
|
||||||
>
|
>
|
||||||
<div style="min-width: var(--w-desk-fixed)" class="overflow-auto">
|
<div style="min-width: var(--w-desk-fixed)" class="overflow-auto">
|
||||||
<Cashflow
|
<Cashflow
|
||||||
class="p-4"
|
class="p-4"
|
||||||
:common-period="period"
|
:common-period="period"
|
||||||
|
:darkMode="darkMode"
|
||||||
@period-change="handlePeriodChange"
|
@period-change="handlePeriodChange"
|
||||||
/>
|
/>
|
||||||
<hr />
|
<hr class="dark:border-gray-800" />
|
||||||
<div class="flex w-full">
|
<div class="flex w-full">
|
||||||
<UnpaidInvoices
|
<UnpaidInvoices
|
||||||
:schema-name="'SalesInvoice'"
|
:schema-name="'SalesInvoice'"
|
||||||
:common-period="period"
|
:common-period="period"
|
||||||
class="border-e"
|
:darkMode="darkMode"
|
||||||
|
class="border-e dark:border-gray-800"
|
||||||
@period-change="handlePeriodChange"
|
@period-change="handlePeriodChange"
|
||||||
/>
|
/>
|
||||||
<UnpaidInvoices
|
<UnpaidInvoices
|
||||||
:schema-name="'PurchaseInvoice'"
|
:schema-name="'PurchaseInvoice'"
|
||||||
:common-period="period"
|
:common-period="period"
|
||||||
|
:darkMode="darkMode"
|
||||||
@period-change="handlePeriodChange"
|
@period-change="handlePeriodChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr class="dark:border-gray-800" />
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ProfitAndLoss
|
<ProfitAndLoss
|
||||||
class="w-full p-4 border-e"
|
class="w-full p-4 border-e dark:border-gray-800"
|
||||||
:common-period="period"
|
:common-period="period"
|
||||||
|
:darkMode="darkMode"
|
||||||
@period-change="handlePeriodChange"
|
@period-change="handlePeriodChange"
|
||||||
/>
|
/>
|
||||||
<Expenses
|
<Expenses
|
||||||
class="w-full p-4"
|
class="w-full p-4"
|
||||||
:common-period="period"
|
:common-period="period"
|
||||||
|
:darkMode="darkMode"
|
||||||
@period-change="handlePeriodChange"
|
@period-change="handlePeriodChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr class="dark:border-gray-800" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -82,6 +80,9 @@ export default {
|
|||||||
PeriodSelector,
|
PeriodSelector,
|
||||||
UnpaidInvoices,
|
UnpaidInvoices,
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
darkMode: Boolean,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return { period: 'This Year' };
|
return { period: 'This Year' };
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<div v-show="hasData" class="flex relative">
|
<div v-show="hasData" class="flex relative">
|
||||||
<!-- Chart Legend -->
|
<!-- Chart Legend -->
|
||||||
<div class="w-1/2 flex flex-col gap-4 justify-center">
|
<div class="w-1/2 flex flex-col gap-4 justify-center dark:text-gray-25">
|
||||||
<!-- Ledgend Item -->
|
<!-- Ledgend Item -->
|
||||||
<div
|
<div
|
||||||
v-for="(d, i) in expenses"
|
v-for="(d, i) in expenses"
|
||||||
@ -36,6 +36,7 @@
|
|||||||
:text-offset-x="6.5"
|
:text-offset-x="6.5"
|
||||||
:value-formatter="(value: number) => fyo.format(value, 'Currency')"
|
:value-formatter="(value: number) => fyo.format(value, 'Currency')"
|
||||||
:total-label="t`Total Spending`"
|
:total-label="t`Total Spending`"
|
||||||
|
:darkMode="darkMode"
|
||||||
@change="(value: number) => (active = value)"
|
@change="(value: number) => (active = value)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -45,7 +46,7 @@
|
|||||||
v-if="expenses.length === 0"
|
v-if="expenses.length === 0"
|
||||||
class="flex-1 w-full h-full flex-center my-20"
|
class="flex-1 w-full h-full flex-center my-20"
|
||||||
>
|
>
|
||||||
<span class="text-base text-gray-600">
|
<span class="text-base text-gray-600 dark:text-gray-500">
|
||||||
{{ t`No expenses in this period` }}
|
{{ t`No expenses in this period` }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -76,14 +77,17 @@ export default defineComponent({
|
|||||||
PeriodSelector,
|
PeriodSelector,
|
||||||
SectionHeader,
|
SectionHeader,
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
darkMode: Boolean,
|
||||||
|
},
|
||||||
extends: DashboardChartBase,
|
extends: DashboardChartBase,
|
||||||
data: () => ({
|
data: () => ({
|
||||||
active: null as null | number,
|
active: null as null | number,
|
||||||
expenses: [] as {
|
expenses: [] as {
|
||||||
account: string;
|
account: string;
|
||||||
total: number;
|
total: number;
|
||||||
color: string;
|
color: { color: string; darkColor: string };
|
||||||
class: string;
|
class: { class: string; darkClass: string };
|
||||||
}[],
|
}[],
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
@ -93,7 +97,11 @@ export default defineComponent({
|
|||||||
hasData(): boolean {
|
hasData(): boolean {
|
||||||
return this.expenses.length > 0;
|
return this.expenses.length > 0;
|
||||||
},
|
},
|
||||||
sectors(): { color: string; label: string; value: number }[] {
|
sectors(): {
|
||||||
|
color: { color: string; darkColor: string };
|
||||||
|
label: string;
|
||||||
|
value: number;
|
||||||
|
}[] {
|
||||||
return this.expenses.map(({ account, color, total }) => ({
|
return this.expenses.map(({ account, color, total }) => ({
|
||||||
color,
|
color,
|
||||||
label: truncate(account, { length: 21 }),
|
label: truncate(account, { length: 21 }),
|
||||||
@ -111,7 +119,6 @@ export default defineComponent({
|
|||||||
fromDate.toISO(),
|
fromDate.toISO(),
|
||||||
toDate.toISO()
|
toDate.toISO()
|
||||||
);
|
);
|
||||||
|
|
||||||
const shades = [
|
const shades = [
|
||||||
{ class: 'bg-pink-500', hex: uicolors.pink['500'] },
|
{ class: 'bg-pink-500', hex: uicolors.pink['500'] },
|
||||||
{ class: 'bg-pink-400', hex: uicolors.pink['400'] },
|
{ class: 'bg-pink-400', hex: uicolors.pink['400'] },
|
||||||
@ -120,13 +127,25 @@ export default defineComponent({
|
|||||||
{ class: 'bg-pink-100', hex: uicolors.pink['100'] },
|
{ class: 'bg-pink-100', hex: uicolors.pink['100'] },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const darkshades = [
|
||||||
|
{ class: 'bg-pink-600', hex: uicolors.pink['600'] },
|
||||||
|
{ class: 'bg-pink-500', hex: uicolors.pink['500'] },
|
||||||
|
{ class: 'bg-pink-400', hex: uicolors.pink['400'] },
|
||||||
|
{ class: 'bg-pink-300', hex: uicolors.pink['300'] },
|
||||||
|
{
|
||||||
|
class: 'bg-pink-200 dark:bg-opacity-80',
|
||||||
|
hex: uicolors.pink['200'] + 'CC',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
this.expenses = topExpenses
|
this.expenses = topExpenses
|
||||||
.filter((e) => e.total > 0)
|
.filter((e) => e.total > 0)
|
||||||
.map((d, i) => {
|
.map((d, i) => {
|
||||||
return {
|
return {
|
||||||
...d,
|
account: d.account,
|
||||||
color: shades[i].hex,
|
total: d.total,
|
||||||
class: shades[i].class,
|
color: { color: shades[i].hex, darkColor: darkshades[i].hex },
|
||||||
|
class: { class: shades[i].class, darkClass: darkshades[i].class },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -9,19 +9,12 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="
|
class="text-sm flex focus:outline-none hover:text-gray-800 dark:hover:text-gray-100 focus:text-gray-800 dark:focus:text-gray-100 items-center py-1 rounded-md leading-relaxed cursor-pointer"
|
||||||
text-sm
|
:class="
|
||||||
flex
|
!value
|
||||||
focus:outline-none
|
? 'text-gray-600 dark:text-gray-500'
|
||||||
hover:text-gray-800
|
: 'text-gray-900 dark:text-gray-300'
|
||||||
focus:text-gray-800
|
|
||||||
items-center
|
|
||||||
py-1
|
|
||||||
rounded-md
|
|
||||||
leading-relaxed
|
|
||||||
cursor-pointer
|
|
||||||
"
|
"
|
||||||
:class="!value ? 'text-gray-600' : 'text-gray-900'"
|
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@click="toggleDropdown()"
|
@click="toggleDropdown()"
|
||||||
@keydown.down="highlightItemDown"
|
@keydown.down="highlightItemDown"
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
class="mt-4"
|
class="mt-4"
|
||||||
:aspect-ratio="2.05"
|
:aspect-ratio="2.05"
|
||||||
:colors="chartData.colors"
|
:colors="chartData.colors"
|
||||||
|
:gridColor="chartData.gridColor"
|
||||||
|
:fontColor="chartData.fontColor"
|
||||||
:points="chartData.points"
|
:points="chartData.points"
|
||||||
:x-labels="chartData.xLabels"
|
:x-labels="chartData.xLabels"
|
||||||
:format="chartData.format"
|
:format="chartData.format"
|
||||||
@ -23,7 +25,7 @@
|
|||||||
:y-min="chartData.yMin"
|
:y-min="chartData.yMin"
|
||||||
/>
|
/>
|
||||||
<div v-else class="flex-1 w-full h-full flex-center my-20">
|
<div v-else class="flex-1 w-full h-full flex-center my-20">
|
||||||
<span class="text-base text-gray-600">
|
<span class="text-base text-gray-600 dark:text-gray-500">
|
||||||
{{ t`No transactions yet` }}
|
{{ t`No transactions yet` }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -53,6 +55,9 @@ export default defineComponent({
|
|||||||
SectionHeader,
|
SectionHeader,
|
||||||
BarChart,
|
BarChart,
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
darkMode: Boolean,
|
||||||
|
},
|
||||||
extends: DashboardChartBase,
|
extends: DashboardChartBase,
|
||||||
data: () => ({
|
data: () => ({
|
||||||
data: [] as { yearmonth: string; balance: number }[],
|
data: [] as { yearmonth: string; balance: number }[],
|
||||||
@ -63,7 +68,10 @@ export default defineComponent({
|
|||||||
chartData() {
|
chartData() {
|
||||||
const points = [this.data.map((d) => d.balance)];
|
const points = [this.data.map((d) => d.balance)];
|
||||||
const colors = [
|
const colors = [
|
||||||
{ positive: uicolors.blue['500'], negative: uicolors.pink['500'] },
|
{
|
||||||
|
positive: uicolors.blue[this.darkMode ? '600' : '500'],
|
||||||
|
negative: uicolors.pink[this.darkMode ? '600' : '500'],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const format = (value: number) => fyo.format(value ?? 0, 'Currency');
|
const format = (value: number) => fyo.format(value ?? 0, 'Currency');
|
||||||
const yMax = getYMax(points);
|
const yMax = getYMax(points);
|
||||||
@ -76,6 +84,9 @@ export default defineComponent({
|
|||||||
yMax,
|
yMax,
|
||||||
yMin,
|
yMin,
|
||||||
formatX: formatXLabels,
|
formatX: formatXLabels,
|
||||||
|
gridColor: this.darkMode ? 'rgba(200, 200, 200, 0.2)' : undefined,
|
||||||
|
fontColor: this.darkMode ? uicolors.gray['400'] : undefined,
|
||||||
|
zeroLineColor: this.darkMode ? uicolors.gray['400'] : undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-baseline justify-between">
|
<div class="flex items-baseline justify-between dark:text-white">
|
||||||
<span class="font-semibold text-base"><slot name="title"></slot></span>
|
<span class="font-semibold text-base"><slot name="title"></slot></span>
|
||||||
<slot name="action"></slot>
|
<slot name="action"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,34 +14,38 @@
|
|||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<!-- Paid -->
|
<!-- Paid -->
|
||||||
<div
|
<div
|
||||||
class="text-sm font-medium"
|
class="text-sm font-medium dark:text-gray-25"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-gray-200 text-gray-200 rounded': !count,
|
'bg-gray-200 dark:bg-gray-700 text-gray-200 dark:text-gray-700 rounded':
|
||||||
|
!count,
|
||||||
'cursor-pointer': paidCount > 0,
|
'cursor-pointer': paidCount > 0,
|
||||||
}"
|
}"
|
||||||
:title="paidCount > 0 ? t`View Paid Invoices` : ''"
|
:title="paidCount > 0 ? t`View Paid Invoices` : ''"
|
||||||
@click="() => routeToInvoices('paid')"
|
@click="() => routeToInvoices('paid')"
|
||||||
>
|
>
|
||||||
{{ fyo.format(paid, 'Currency') }}
|
{{ fyo.format(paid, 'Currency') }}
|
||||||
<span :class="{ 'text-gray-900 font-normal': count }">{{
|
<span
|
||||||
t`Paid`
|
:class="{ 'text-gray-900 dark:text-gray-200 font-normal': count }"
|
||||||
}}</span>
|
>{{ t`Paid` }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Unpaid -->
|
<!-- Unpaid -->
|
||||||
<div
|
<div
|
||||||
class="text-sm font-medium"
|
class="text-sm font-medium dark:text-gray-25"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-gray-200 text-gray-200 rounded': !count,
|
'bg-gray-200 dark:bg-gray-700 text-gray-200 dark:text-gray-700 rounded':
|
||||||
|
!count,
|
||||||
'cursor-pointer': unpaidCount > 0,
|
'cursor-pointer': unpaidCount > 0,
|
||||||
}"
|
}"
|
||||||
:title="unpaidCount > 0 ? t`View Unpaid Invoices` : ''"
|
:title="unpaidCount > 0 ? t`View Unpaid Invoices` : ''"
|
||||||
@click="() => routeToInvoices('unpaid')"
|
@click="() => routeToInvoices('unpaid')"
|
||||||
>
|
>
|
||||||
{{ fyo.format(unpaid, 'Currency') }}
|
{{ fyo.format(unpaid, 'Currency') }}
|
||||||
<span :class="{ 'text-gray-900 font-normal': count }">{{
|
<span
|
||||||
t`Unpaid`
|
:class="{ 'text-gray-900 dark:text-gray-200 font-normal': count }"
|
||||||
}}</span>
|
>{{ t`Unpaid` }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -64,7 +68,7 @@
|
|||||||
:offset="15"
|
:offset="15"
|
||||||
:show="show"
|
:show="show"
|
||||||
placement="top"
|
placement="top"
|
||||||
class="text-sm shadow-md px-2 py-1 bg-white text-gray-900 border-s-4"
|
class="text-sm shadow-md px-2 py-1 bg-white dark:bg-gray-900 text-gray-900 dark:text-white border-s-4"
|
||||||
:style="{ borderColor: colors }"
|
:style="{ borderColor: colors }"
|
||||||
>
|
>
|
||||||
<div class="flex justify-between gap-4">
|
<div class="flex justify-between gap-4">
|
||||||
@ -110,6 +114,7 @@ export default defineComponent({
|
|||||||
extends: BaseDashboardChart,
|
extends: BaseDashboardChart,
|
||||||
props: {
|
props: {
|
||||||
schemaName: { type: String as PropType<string>, required: true },
|
schemaName: { type: String as PropType<string>, required: true },
|
||||||
|
darkMode: Boolean,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -144,25 +149,24 @@ export default defineComponent({
|
|||||||
if (this.schemaName === ModelNameEnum.SalesInvoice) {
|
if (this.schemaName === ModelNameEnum.SalesInvoice) {
|
||||||
return 'blue';
|
return 'blue';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'pink';
|
return 'pink';
|
||||||
},
|
},
|
||||||
colors(): string {
|
colors(): string {
|
||||||
return uicolors[this.color]['500'];
|
return uicolors[this.color][this.darkMode ? '600' : '500'];
|
||||||
},
|
},
|
||||||
paidColor(): string {
|
paidColor(): string {
|
||||||
if (!this.hasData) {
|
if (!this.hasData) {
|
||||||
return 'bg-gray-400';
|
return this.darkMode ? 'bg-gray-700' : 'bg-gray-400';
|
||||||
}
|
}
|
||||||
|
|
||||||
return `bg-${this.color}-500`;
|
return `bg-${this.color}-${this.darkMode ? '600' : '500'}`;
|
||||||
},
|
},
|
||||||
unpaidColor(): string {
|
unpaidColor(): string {
|
||||||
if (!this.hasData) {
|
if (!this.hasData) {
|
||||||
return 'bg-gray-200';
|
return `bg-gray-${this.darkMode ? '800' : '200'}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `bg-${this.color}-200`;
|
return `bg-${this.color}-${this.darkMode ? '700 bg-opacity-20' : '200'}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async activated() {
|
async activated() {
|
||||||
|
@ -8,15 +8,22 @@ import { toggleSidebar } from 'src/utils/ui';
|
|||||||
<!-- eslint-disable vue/require-explicit-emits -->
|
<!-- eslint-disable vue/require-explicit-emits -->
|
||||||
<Sidebar
|
<Sidebar
|
||||||
v-show="showSidebar"
|
v-show="showSidebar"
|
||||||
class="flex-shrink-0 border-e whitespace-nowrap w-sidebar"
|
class="flex-shrink-0 border-e dark:border-gray-800 whitespace-nowrap w-sidebar"
|
||||||
|
:darkMode="darkMode"
|
||||||
@change-db-file="$emit('change-db-file')"
|
@change-db-file="$emit('change-db-file')"
|
||||||
|
@toggle-darkmode="$emit('toggle-darkmode')"
|
||||||
/>
|
/>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<div class="flex flex-1 overflow-y-hidden bg-white">
|
<div class="flex flex-1 overflow-y-hidden bg-white dark:bg-gray-875">
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<component :is="Component" :key="$route.path" class="flex-1" />
|
<component
|
||||||
|
:is="Component"
|
||||||
|
:key="$route.path"
|
||||||
|
:darkMode="darkMode"
|
||||||
|
class="flex-1"
|
||||||
|
/>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</router-view>
|
</router-view>
|
||||||
|
|
||||||
@ -26,6 +33,7 @@ import { toggleSidebar } from 'src/utils/ui';
|
|||||||
<component
|
<component
|
||||||
:is="Component"
|
:is="Component"
|
||||||
:key="route.query.schemaName + route.query.name"
|
:key="route.query.schemaName + route.query.name"
|
||||||
|
:darkMode="darkMode"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
@ -35,19 +43,7 @@ import { toggleSidebar } from 'src/utils/ui';
|
|||||||
<!-- Show Sidebar Button -->
|
<!-- Show Sidebar Button -->
|
||||||
<button
|
<button
|
||||||
v-show="!showSidebar"
|
v-show="!showSidebar"
|
||||||
class="
|
class="absolute bottom-0 start-0 text-gray-600 bg-gray-100 rounded rtl-rotate-180 p-1 m-4 opacity-0 hover:opacity-100 hover:shadow-md"
|
||||||
absolute
|
|
||||||
bottom-0
|
|
||||||
start-0
|
|
||||||
text-gray-600
|
|
||||||
bg-gray-100
|
|
||||||
rounded
|
|
||||||
rtl-rotate-180
|
|
||||||
p-1
|
|
||||||
m-4
|
|
||||||
opacity-0
|
|
||||||
hover:opacity-100 hover:shadow-md
|
|
||||||
"
|
|
||||||
@click="() => toggleSidebar()"
|
@click="() => toggleSidebar()"
|
||||||
>
|
>
|
||||||
<feather-icon name="chevrons-right" class="w-4 h-4" />
|
<feather-icon name="chevrons-right" class="w-4 h-4" />
|
||||||
@ -62,7 +58,10 @@ export default defineComponent({
|
|||||||
components: {
|
components: {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
},
|
},
|
||||||
emits: ['change-db-file'],
|
props: {
|
||||||
|
darkMode: Boolean,
|
||||||
|
},
|
||||||
|
emits: ['change-db-file', 'toggle-darkmode'],
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import registerIpcRendererListeners from './renderer/registerIpcRendererListener
|
|||||||
import router from './router';
|
import router from './router';
|
||||||
import { stringifyCircular } from './utils';
|
import { stringifyCircular } from './utils';
|
||||||
import { setLanguageMap } from './utils/language';
|
import { setLanguageMap } from './utils/language';
|
||||||
|
import { setDarkMode } from './utils/theme';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
(async () => {
|
(async () => {
|
||||||
@ -20,6 +21,9 @@ import { setLanguageMap } from './utils/language';
|
|||||||
}
|
}
|
||||||
fyo.store.language = language || 'English';
|
fyo.store.language = language || 'English';
|
||||||
|
|
||||||
|
const darkMode = fyo.config.get('darkMode') as boolean;
|
||||||
|
setDarkMode(darkMode);
|
||||||
|
|
||||||
registerIpcRendererListeners();
|
registerIpcRendererListeners();
|
||||||
const { isDevelopment, platform, version } = await ipc.getEnv();
|
const { isDevelopment, platform, version } = await ipc.getEnv();
|
||||||
|
|
||||||
|
20
src/utils/theme.ts
Normal file
20
src/utils/theme.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { fyo } from 'src/initFyo';
|
||||||
|
|
||||||
|
export async function toggleDarkMode(): Promise<void> {
|
||||||
|
const darkMode = fyo.config.get('darkMode');
|
||||||
|
if (darkMode) {
|
||||||
|
document.documentElement.classList.remove('dark');
|
||||||
|
fyo.config.set('darkMode', false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
fyo.config.set('darkMode', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setDarkMode(darkMode: boolean): void {
|
||||||
|
if (darkMode) {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.documentElement.classList.remove('dark');
|
||||||
|
}
|
@ -4,6 +4,7 @@ const colors = JSON.parse(
|
|||||||
);
|
);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
darkMode: 'class',
|
||||||
purge: false,
|
purge: false,
|
||||||
theme: {
|
theme: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
@ -66,7 +67,14 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
margin: ['responsive', 'first', 'last', 'hover', 'focus'],
|
margin: ['responsive', 'first', 'last', 'hover', 'focus'],
|
||||||
backgroundColor: ['responsive', 'first', 'hover', 'focus', 'focus-within'],
|
backgroundColor: [
|
||||||
|
'responsive',
|
||||||
|
'first',
|
||||||
|
'hover',
|
||||||
|
'focus',
|
||||||
|
'focus-within',
|
||||||
|
'dark',
|
||||||
|
],
|
||||||
display: ['group-hover'],
|
display: ['group-hover'],
|
||||||
},
|
},
|
||||||
plugins: [require('tailwindcss-rtl')],
|
plugins: [require('tailwindcss-rtl')],
|
||||||
|
Loading…
Reference in New Issue
Block a user