Skip to content

Commit ba9b4d7

Browse files
authored
Merge pull request #301 from ury-erp/feat/dashboard-links-mosaic-header
Feat/dashboard links mosaic header
2 parents 79b64d7 + dd76946 commit ba9b4d7

7 files changed

Lines changed: 154 additions & 42 deletions

File tree

frontend/src/pages/Dashboard/KPIGrid.tsx

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@ import React from 'react';
22
import { formatCurrency } from '@ury/core';
33
import { Card, Spinner } from '@ury/ui';
44
import { DashboardSummary } from '../../services/dashboard';
5+
import uryPosLogo from '../../../../pos/public/ury_pos.png';
6+
import uryMosaicLogo from '../../../../mosaic/src/assets/logos/mosaic.jpg';
7+
8+
interface LinkCardProps {
9+
logoSrc: string;
10+
label: string;
11+
href: string;
12+
}
13+
14+
const LinkCard: React.FC<LinkCardProps> = ({ logoSrc, label, href }) => {
15+
return (
16+
<a href={href} className="block outline-none h-full">
17+
<Card className="h-full rounded-lg border border-gray-200 bg-white p-5 shadow-xs transition-all duration-200 hover:shadow-md hover:border-primary/20">
18+
<div className="flex h-4 items-center mb-2">
19+
<img src={logoSrc} alt="Logo" className="h-full object-contain opacity-70 mix-blend-multiply" />
20+
</div>
21+
<h3 className="mt-2 text-2xl font-bold text-gray-900 tracking-tight">{label}</h3>
22+
</Card>
23+
</a>
24+
);
25+
};
526

627
interface KPIGridProps {
728
summary: DashboardSummary | null;
@@ -45,6 +66,18 @@ export const KPIGrid: React.FC<KPIGridProps> = ({ summary, loading }) => {
4566
return (
4667
<section className="w-full">
4768
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
69+
<LinkCard
70+
logoSrc={uryPosLogo}
71+
label="URY POS"
72+
href="/pos"
73+
/>
74+
75+
<LinkCard
76+
logoSrc={uryMosaicLogo}
77+
label="URY MOSAIC"
78+
href="/mosaic"
79+
/>
80+
4881
<KPICard
4982
title="Today's Sales"
5083
value={formatCurrency(todaySales)}
@@ -69,12 +102,6 @@ export const KPIGrid: React.FC<KPIGridProps> = ({ summary, loading }) => {
69102
loading={loading}
70103
/>
71104

72-
<KPICard
73-
title="Active Menu Items"
74-
value={`${totalMenuItems} Items`}
75-
loading={loading}
76-
/>
77-
78105
<KPICard
79106
title="Average Order Value"
80107
value={formatCurrency(aov)}
@@ -86,12 +113,6 @@ export const KPIGrid: React.FC<KPIGridProps> = ({ summary, loading }) => {
86113
value={`${pendingOrders} KOTs`}
87114
loading={loading}
88115
/>
89-
90-
<KPICard
91-
title="Active Cashiers"
92-
value={`${activeCashiers} Online`}
93-
loading={loading}
94-
/>
95116
</div>
96117
</section>
97118
);

mosaic/src/components/Header.vue

Lines changed: 110 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,135 @@
11
<template>
2-
3-
<header class="bg-white p-4 flex justify-between items-center">
2+
<header class="bg-white p-4 flex justify-between items-center border-b border-gray-200">
43
<router-link to="/" class="flex items-center cursor-pointer">
5-
<img :src="imagePath" alt="Logo" class="ml-20 w-40 h-15 mr-2">
4+
<img :src="imagePath" alt="Logo" class="ml-20 w-40 h-15 mr-2">
65
</router-link>
7-
<div class="flex items-center">
6+
<div class="flex items-center gap-4">
7+
<button
8+
class="flex justify-center items-center h-12 w-12 rounded-xl hover:bg-slate-200 transition-colors text-blue-800"
9+
@click="reloadKOT"
10+
>
11+
<svg class="w-6 h-6" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20">
12+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 1v5h-5M2 19v-5h5m10-4a8 8 0 0 1-14.947 3.97M1 10a8 8 0 0 1 14.947-3.97"/>
13+
</svg>
14+
</button>
815

16+
<!-- Reused POS User Dropdown -->
17+
<div class="relative" ref="userMenuRef">
18+
<button
19+
@click="toggleUserMenu"
20+
class="flex items-center gap-2 px-4 h-12 rounded-xl hover:bg-slate-200 transition-colors text-gray-700 hover:text-gray-900"
21+
>
22+
<div class="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center">
23+
<svg class="w-4 h-4 text-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
24+
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/>
25+
<circle cx="12" cy="7" r="4"/>
26+
</svg>
27+
</div>
28+
<span class="text-sm font-semibold">{{ userName }}</span>
29+
<svg class="w-4 h-4 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
30+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
31+
</svg>
32+
</button>
933

10-
<button class=" hover:bg-slate-300 text-blue font-semibold px-6 py-1 rounded-md" @click="reloadKOT">
11-
<svg class="w-6 h-6 text-blue-800 dark:text-blue" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20">
12-
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 1v5h-5M2 19v-5h5m10-4a8 8 0 0 1-14.947 3.97M1 10a8 8 0 0 1 14.947-3.97"/> Refresh
13-
</svg>
34+
<div v-if="showUserMenu" class="absolute right-0 mt-2 w-56 bg-white rounded-lg shadow-lg border border-gray-200 z-50">
35+
<div class="p-4 border-b border-gray-200">
36+
<p class="text-sm font-semibold text-gray-900">{{ userName }}</p>
37+
<p class="text-sm font-semibold text-gray-500">{{ userId }}</p>
38+
</div>
39+
<div class="py-2">
40+
<button
41+
@click="switchToDashboard"
42+
class="flex justify-start items-center w-full px-4 py-2 text-sm font-semibold text-gray-700 hover:bg-gray-100 transition-colors"
43+
>
44+
<svg class="w-4 h-4 mr-3 text-gray-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
45+
<rect width="20" height="14" x="2" y="3" rx="2"/>
46+
<line x1="8" x2="16" y1="21" y2="21"/>
47+
<line x1="12" x2="12" y1="17" y2="21"/>
48+
</svg>
49+
Switch to Dashboard
50+
</button>
1451

15-
</button>
52+
<button
53+
@click="logout"
54+
class="flex justify-start items-center w-full px-4 py-2 text-sm font-semibold text-red-600 hover:bg-red-50 hover:text-red-700 transition-colors"
55+
>
56+
<svg class="w-4 h-4 mr-3 text-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
57+
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>
58+
<polyline points="16 17 21 12 16 7"/>
59+
<line x1="21" x2="9" y1="12" y2="12"/>
60+
</svg>
61+
Logout
62+
</button>
63+
</div>
64+
</div>
65+
</div>
1666
</div>
17-
</header>
18-
19-
67+
</header>
2068
</template>
2169

2270
<script>
23-
2471
import urimosaicImage from "@/assets/logos/mosaic.jpg";
25-
// import KOT from './kot.vue';
2672
2773
export default {
2874
name: "Header",
29-
setup() {
30-
31-
},
3275
data() {
3376
return {
3477
imagePath: urimosaicImage,
78+
showUserMenu: false,
79+
userName: "User",
80+
userId: "user@example.com"
3581
};
3682
},
37-
methods:{
38-
reloadKOT(){
39-
// KOT.methods.fetchKOT(this);
83+
methods: {
84+
reloadKOT() {
4085
window.location.reload();
86+
},
87+
toggleUserMenu() {
88+
this.showUserMenu = !this.showUserMenu;
89+
},
90+
switchToDashboard() {
91+
this.showUserMenu = false;
92+
window.location.href = '/ury/dashboard';
93+
},
94+
async logout() {
95+
this.showUserMenu = false;
96+
try {
97+
await fetch('/api/method/logout', { method: 'POST' });
98+
} catch (e) {
99+
console.error(e);
100+
}
101+
window.location.href = '/login?redirect-to=%2Fmosaic';
102+
},
103+
handleClickOutside(event) {
104+
if (this.$refs.userMenuRef && !this.$refs.userMenuRef.contains(event.target)) {
105+
this.showUserMenu = false;
106+
}
107+
},
108+
async fetchUser() {
109+
try {
110+
const res = await fetch('/api/method/frappe.auth.get_logged_user');
111+
const data = await res.json();
112+
if (data.message) {
113+
this.userId = data.message;
114+
const userRes = await fetch(`/api/method/frappe.client.get_value?doctype=User&filters={"name":"${data.message}"}&fieldname=full_name`);
115+
const userData = await userRes.json();
116+
if (userData.message && userData.message.full_name) {
117+
this.userName = userData.message.full_name;
118+
} else {
119+
this.userName = data.message;
120+
}
121+
}
122+
} catch (err) {
123+
console.error("Failed to fetch user", err);
124+
}
41125
}
42126
},
43-
computed: {
44-
127+
mounted() {
128+
document.addEventListener('mousedown', this.handleClickOutside);
129+
this.fetchUser();
45130
},
131+
unmounted() {
132+
document.removeEventListener('mousedown', this.handleClickOutside);
133+
}
46134
};
47135
</script>

pos/src/components/Header.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,14 @@ const Header = () => {
174174
<Lock className="w-4 h-4 me-3" />
175175
{t('header.close_shift')}
176176
</Button>
177+
<Button
178+
variant="ghost"
179+
className="flex justify-start items-center w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors"
180+
onClick={() => window.location.href = '/ury/dashboard'}
181+
>
182+
<Monitor className="w-4 h-4 me-3" />
183+
Switch to Dashboard
184+
</Button>
177185
<Button
178186
variant="ghost"
179187
className="flex justify-start items-center w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors"

pos/src/lib/order-api.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ export interface TableOrder {
5454
* @returns The order details and customer information if an active order exists
5555
*/
5656
export async function getTableOrder(table_no: string): Promise<TableOrder> {
57-
const { call } = await import('@ury/core');
5857
try {
5958
const res = await call.get('ury.ury.doctype.ury_order.ury_order.get_order_invoice', {
6059
table: table_no

pos/src/lib/table-api.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DOCTYPES } from '../data/doctypes';
2-
import { db } from '@ury/core';
2+
import { db, call } from '@ury/core';
33

44
export interface Room {
55
name: string;
@@ -22,7 +22,6 @@ export interface Table {
2222

2323

2424
export async function getRestaurantMenu(posProfile: string, room?: string | null) {
25-
const { call } = await import('@ury/core');
2625
const params: Record<string, string> = { pos_profile: posProfile };
2726
if (room) {
2827
params.room = room;
@@ -124,15 +123,13 @@ export async function updateTableLayout(name: string, data: Partial<Table>) {
124123
}
125124

126125
export async function mergeTablesBatch(anchor: string, tables: string[]) {
127-
const { call } = await import('@ury/core');
128126
return call.post('ury.ury.doctype.ury_order.ury_order.merge_tables_batch', {
129127
anchor_table: anchor,
130128
tables,
131129
});
132130
}
133131

134132
export async function unmergeTables(table: string) {
135-
const { call } = await import('@ury/core');
136133
return call.post('ury.ury.doctype.ury_order.ury_order.unmerge_tables', {
137134
table,
138135
});

pos/src/pages/Dashboard.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { TrendingUp, ShoppingCart, Clock, Users, AlertTriangle, Bell } from 'luc
22
import { Card, CardContent } from '@ury/ui';
33
import { useState, useEffect } from 'react';
44
import { usePOSStore } from '../store/pos-store';
5-
import { formatCurrency } from '@ury/core';
5+
import { formatCurrency, call } from '@ury/core';
66
import HufLogo from '../components/HufLogo';
77

88
// Helper function to format relative time
@@ -58,7 +58,6 @@ export default function Dashboard() {
5858
if (!posProfile?.branch) return;
5959

6060
const fetchDashboardData = async () => {
61-
const { call } = await import('@ury/core');
6261

6362
// Fetch dashboard stats
6463
setStatsLoading(true);

ury/ury_pos/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1256,7 +1256,7 @@ def get_pos_opening_screen_data() -> dict:
12561256
filters={
12571257
"user": user,
12581258
"docstatus": 1,
1259-
"pos_closing_entry": ["in", ["", None]],
1259+
"status": "Open",
12601260
},
12611261
fields=[
12621262
"name",

0 commit comments

Comments
 (0)