Added dashboard framework. Components are not yet created nor is the query finalized. BOD-79

Missed in previous commit. BOD-79
This commit is contained in:
Patrick Fic
2020-07-14 15:21:38 -07:00
parent e91751e20c
commit 2eb8360f5d
28 changed files with 753 additions and 85 deletions

View File

@@ -4684,6 +4684,152 @@
</folder_node>
</children>
</folder_node>
<folder_node>
<name>dashboard</name>
<children>
<folder_node>
<name>actions</name>
<children>
<concept_node>
<name>addcomponent</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>updatinglayout</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
<name>titles</name>
<children>
<concept_node>
<name>monthlyrevenuegraph</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>productiondollars</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>productionhours</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>projectedmonthlysales</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
</children>
</folder_node>
<folder_node>
<name>documents</name>
<children>

View File

@@ -2,8 +2,8 @@ import axios from "axios";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { auth } from "../../firebase/firebase.utils";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -97,6 +97,13 @@ export default connect(
<button onClick={handle}>Hit with Header.</button>
<button onClick={handleLocal}>Hit Localhost with Header.</button>
<button onClick={handleQbxml}>Qbxml</button>
<button
onClick={() => {
logImEXEvent("IMEXEVENT", { somethignArThare: 5 });
}}
>
Log an ImEX Event.
</button>
</div>
);
});

View File

@@ -0,0 +1,82 @@
import { Card } from "antd";
import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import {
Area,
Bar,
CartesianGrid,
ComposedChart,
Legend,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis
} from "recharts";
import * as Utils from "../../scoreboard-targets-table/scoreboard-targets-table.util";
export default function DashboardMonthlyRevenueGraph({ data, ...cardProps }) {
const { t } = useTranslation();
const jobsByDate = {
"2020-07-5": [{ clm_total: 1224 }],
"2020-07-8": [{ clm_total: 987 }, { clm_total: 8755 }],
"2020-07-12": [{ clm_total: 684 }, { clm_total: 12022 }],
"2020-07-21": [{ clm_total: 15000 }],
"2020-07-28": [{ clm_total: 122 }, { clm_total: 4522 }],
};
const listOfDays = Utils.ListOfDaysInCurrentMonth();
const chartData = listOfDays.reduce((acc, val) => {
//Sum up the current day.
let dailySales;
if (!!jobsByDate[val]) {
dailySales = jobsByDate[val].reduce((dayAcc, dayVal) => {
return dayAcc + dayVal.clm_total;
}, 0);
} else {
dailySales = 0;
}
const theValue = {
date: moment(val).format("D dd"),
dailySales,
accSales:
acc.length > 0 ? acc[acc.length - 1].accSales + dailySales : dailySales,
};
return [...acc, theValue];
}, []);
return (
<Card title={t("dashboard.titles.monthlyrevenuegraph")} {...cardProps}>
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
data={chartData}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid stroke="#f5f5f5" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Legend />
<Area
type="monotone"
name="Accumulated Sales"
dataKey="accSales"
fill="#8884d8"
stroke="#8884d8"
/>
<Bar
name="Daily Sales"
dataKey="dailySales"
//stackId="day"
barSize={20}
fill="#413ea0"
/>
</ComposedChart>
</ResponsiveContainer>
</Card>
);
}

View File

@@ -0,0 +1,30 @@
import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons";
import { Card, Statistic } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
export default function DashboardProjectedMonthlySales({ data, ...cardProps }) {
const { t } = useTranslation();
const aboveTargetMonthlySales = false;
return (
<Card {...cardProps}>
<Statistic
title={t("dashboard.titles.projectedmonthlysales")}
value={222000.0}
precision={2}
prefix={
<div>
{aboveTargetMonthlySales ? (
<ArrowUpOutlined />
) : (
<ArrowDownOutlined />
)}
$
</div>
}
valueStyle={{ color: aboveTargetMonthlySales ? "green" : "red" }}
/>
</Card>
);
}

View File

@@ -0,0 +1,33 @@
import React from "react";
import { Card, Statistic } from "antd";
import { useTranslation } from "react-i18next";
import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons";
export default function DashboardTotalProductionDollars({
data,
...cardProps
}) {
const { t } = useTranslation();
const aboveTargetProductionDollars = false;
return (
<Card {...cardProps}>
<Statistic
title={t("dashboard.titles.productiondollars")}
value={175000.0}
precision={2}
prefix={
<div>
{aboveTargetProductionDollars ? (
<ArrowUpOutlined />
) : (
<ArrowDownOutlined />
)}
$
</div>
}
valueStyle={{ color: aboveTargetProductionDollars ? "green" : "red" }}
/>
</Card>
);
}

View File

@@ -0,0 +1,19 @@
import React from "react";
import { Card, Statistic } from "antd";
import { useTranslation } from "react-i18next";
import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons";
export default function DashboardTotalProductionHours({ data, ...cardProps }) {
const { t } = useTranslation();
const aboveTargetHours = true;
return (
<Card {...cardProps}>
<Statistic
title={t("dashboard.titles.productionhours")}
value={750}
prefix={aboveTargetHours ? <ArrowUpOutlined /> : <ArrowDownOutlined />}
valueStyle={{ color: aboveTargetHours ? "green" : "red" }}
/>
</Card>
);
}

View File

@@ -1,64 +1,176 @@
import { Card } from "antd";
import Icon from "@ant-design/icons";
import { Button, Dropdown, Menu, notification } from "antd";
import React, { useState } from "react";
import { useMutation, useQuery } from "react-apollo";
import { Responsive, WidthProvider } from "react-grid-layout";
import styled from "styled-components";
import { useTranslation } from "react-i18next";
import { MdClose } from "react-icons/md";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
import { QUERY_DASHBOARD_DETAILS } from "../../graphql/bodyshop.queries";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import DashboardMonthlyRevenueGraph from "../dashboard-components/monthly-revenue-graph/monthly-revenue-graph.component";
import DashboardProjectedMonthlySales from "../dashboard-components/pojected-monthly-sales/projected-monthly-sales.component";
import DashboardTotalProductionDollars from "../dashboard-components/total-production-dollars/total-production-dollars.component";
import DashboardTotalProductionHours from "../dashboard-components/total-production-hours/total-production-hours.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
//Combination of the following:
// /node_modules/react-grid-layout/css/styles.css
// /node_modules/react-resizable/css/styles.css
import "./dashboard-grid.styles.css";
const Sdiv = styled.div`
position: absolute;
height: 80%;
width: 80%;
top: 10%;
left: 10%;
// background-color: #ffcc00;
`;
import "./dashboard-grid.styles.scss";
const ResponsiveReactGridLayout = WidthProvider(Responsive);
export default function DashboardGridComponent() {
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function DashboardGridComponent({ currentUser, bodyshop }) {
const { loading, error, data } = useQuery(QUERY_DASHBOARD_DETAILS);
const { t } = useTranslation();
const [state, setState] = useState({
layout: [
{ i: "1", x: 0, y: 0, w: 2, h: 2 },
{ i: "2", x: 2, y: 0, w: 2, h: 2 },
{ i: "3", x: 4, y: 0, w: 2, h: 2 }
]
layout: bodyshop.associations[0].user.dashboardlayout || [
{ i: "ProductionDollars", x: 0, y: 0, w: 2, h: 2 },
// { i: "ProductionHours", x: 2, y: 0, w: 2, h: 2 },
],
});
const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
const defaultProps = {
className: "layout",
breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }
// cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
// rowHeight: 100
const handleLayoutChange = async (newLayout) => {
setState({ ...state, layout: newLayout });
const result = await updateLayout({
variables: { email: currentUser.email, layout: newLayout },
});
if (!!result.errors) {
notification["error"]({
message: t("dashboard.errors.updatinglayout", {
message: JSON.stringify(result.errors),
}),
});
}
};
const handleRemoveComponent = (key) => {
const idxToRemove = state.layout.findIndex((i) => i.i === key);
const newLayout = state.layout;
newLayout.splice(idxToRemove, 1);
console.log(newLayout);
handleLayoutChange(newLayout);
};
const handleAddComponent = (e) => {
handleLayoutChange([
...state.layout,
{
i: e.key,
x: (state.layout.length * 2) % (state.cols || 12),
y: Infinity, // puts it at the bottom
w: componentList[e.key].w || 2,
h: componentList[e.key].h || 2,
},
]);
};
// We're using the cols coming back from this to calculate where to add new items.
const onBreakpointChange = (breakpoint, cols) => {
console.log("breakpoint, cols", breakpoint, cols);
// setState({ ...state, breakpoint: breakpoint, cols: cols });
setState({ ...state, breakpoint: breakpoint, cols: cols });
};
if (true) return null;
const existingLayoutKeys = state.layout.map((i) => i.i);
const addComponentOverlay = (
<Menu onClick={handleAddComponent}>
{Object.keys(componentList).map((key) => (
<Menu.Item
key={key}
value={key}
disabled={existingLayoutKeys.includes(key)}
>
{componentList[key].label}
</Menu.Item>
))}
</Menu>
);
return (
<Sdiv>
The Grid.
<div>
<Dropdown overlay={addComponentOverlay} trigger={["click"]}>
<Button>{t("dashboard.actions.addcomponent")}</Button>
</Dropdown>
<ResponsiveReactGridLayout
{...defaultProps}
className="layout"
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
width="100%"
onLayoutChange={handleLayoutChange}
onBreakpointChange={onBreakpointChange}
width='100%'
onLayoutChange={layout => {
console.log("layout", layout);
setState({ ...state, layout });
}}>
>
{state.layout.map((item, index) => {
const TheComponent = componentList[item.i].component;
return (
<Card style={{ width: "100px" }} key={item.i} data-grid={item}>
A Card {index}
</Card>
<div key={item.i} data-grid={item}>
<LoadingSkeleton loading={loading}>
<Icon
component={MdClose}
key={item.i}
style={{
position: "absolute",
zIndex: "2",
right: ".25rem",
top: ".25rem",
cursor: "pointer",
}}
onClick={() => handleRemoveComponent(item.i)}
/>
<TheComponent
className="dashboard-card"
size="small"
style={{ height: "100%", width: "100%" }}
/>
</LoadingSkeleton>
</div>
);
})}
</ResponsiveReactGridLayout>
</Sdiv>
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(DashboardGridComponent);
const componentList = {
ProductionDollars: {
label: "Production Dollars",
component: DashboardTotalProductionDollars,
w: 2,
h: 1,
},
ProductionHours: {
label: "Production Hours",
component: DashboardTotalProductionHours,
w: 2,
h: 1,
},
ProjectedMonthlySales: {
label: "Projected Monthly Sales",
component: DashboardProjectedMonthlySales,
w: 2,
h: 1,
},
MonthlyRevenueGraph: {
label: "Monthly Sales Graph",
component: DashboardMonthlyRevenueGraph,
w: 2,
h: 2,
},
};

View File

@@ -124,3 +124,5 @@
.react-resizable-hide > .react-resizable-handle {
display: none;
}

View File

@@ -0,0 +1,12 @@
.dashboard-card {
// background-color: green;
.ant-card-body {
// background-color: red;
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
}

View File

@@ -8,39 +8,45 @@ class ErrorBoundary extends React.Component {
this.state = {
hasErrored: false,
error: null,
info: null,
};
}
static getDerivedStateFromError(error) {
return { hasErrored: true, error };
console.log("ErrorBoundary -> getDerivedStateFromError -> error", error);
return { hasErrored: true, error: error };
}
componentDidCatch(error, info) {
console.log("Exception Caught by Error Boundary.", error, info);
this.setState({ ...this.state, error, info });
}
render() {
console.log("this.state", this.state);
const { t } = this.props;
if (this.state.hasErrored === true) {
return (
<div>
<Result
status='500'
status="500"
title={t("general.labels.exceptiontitle")}
subTitle={t("general.messages.exception")}
extra={
<Space>
<Button
type='primary'
type="primary"
onClick={() => {
window.location.reload();
}}>
}}
>
{t("general.actions.refresh")}
</Button>
<Button
onClick={() => {
alert("Not implemented yet.");
}}>
}}
>
{t("general.actions.submitticket")}
</Button>
</Space>
@@ -50,7 +56,10 @@ class ErrorBoundary extends React.Component {
<Col offset={6} span={12}>
<Collapse bordered={false}>
<Collapse.Panel header={t("general.labels.errors")}>
{JSON.stringify(this.state.error || "")}
<div>
<strong>{this.state.error.message}</strong>
</div>
<div>{this.state.error.stack}</div>
</Collapse.Panel>
</Collapse>
</Col>

View File

@@ -4,5 +4,9 @@ import "./loading-skeleton.styles.scss";
import { Skeleton } from "antd";
export default function LoadingSkeleton(props) {
return <Skeleton {...props} className='loading-skeleton' active />;
return (
<Skeleton {...props} className="loading-skeleton" active>
{props.children}
</Skeleton>
);
}

View File

@@ -1,21 +1,33 @@
import moment from "moment";
import React from "react";
import { connect } from "react-redux";
import {
ComposedChart,
Line,
Area,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
CartesianGrid, ComposedChart,
Legend, Line,
ResponsiveContainer, Tooltip, XAxis,
YAxis
} from "recharts";
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
import moment from "moment";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,

View File

@@ -3,6 +3,13 @@ import gql from "graphql-tag";
export const QUERY_BODYSHOP = gql`
query QUERY_BODYSHOP {
bodyshops(where: { associations: { active: { _eq: true } } }) {
associations {
user {
authid
email
dashboardlayout
}
}
address1
address2
city
@@ -114,3 +121,36 @@ export const QUERY_STRIPE_ID = gql`
}
}
`;
export const QUERY_DASHBOARD_DETAILS = gql`
query QUERY_DASHBOARD_DETAILS {
query
QUERY_DASHBOARD_DETAILS {
jobs {
id
clm_total
scheduled_completion
date_invoiced
ins_co_nm
}
compJobs: jobs(where: { inproduction: { _eq: true } }) {
id
scheduled_completion
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAB" } }) {
aggregate {
sum {
mod_lb_hrs
}
}
}
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" } }) {
aggregate {
sum {
mod_lb_hrs
}
}
}
}
}
}
`;

View File

@@ -828,6 +828,8 @@ export const QUERY_ALL_JOBS_PAGINATED = gql`
}
`;
export const QUERY_JOB_CLOSE_DETAILS = gql`
query QUERY_JOB_CLOSE_DETAILS($id: uuid!) {
jobs_by_pk(id: $id) {

View File

@@ -13,6 +13,18 @@ export const UPSERT_USER = gql`
}
`;
export const UPDATE_DASHBOARD_LAYOUT = gql`
mutation UPDATE_DASHBOARD_LAYOUT($email: String!, $layout: jsonb!) {
update_users_by_pk(
pk_columns: { email: $email }
_set: { dashboardlayout: $layout }
) {
email
dashboardlayout
}
}
`;
export const UPDATE_FCM_TOKEN = gql`
mutation UPDATE_FCM_TOKEN($authEmail: String!, $token: jsonb!) {
update_users(

View File

@@ -1,34 +1,10 @@
import React from "react";
import DashboardGridComponent from "../../components/dashboard-grid/dashboard-grid.component";
import Test from "../../components/_test/test.component";
import { analytics, logImEXEvent } from "../../firebase/firebase.utils";
export default function ManageRootPageComponent() {
//const client = useApolloClient();
return (
<div>
<Test />
<button
onClick={() => {
logImEXEvent("IMEXEVENT", { somethignArThare: 5 });
}}>
LogImEXEvent
</button>
<button
onClick={() => {
console.log("Things.");
analytics.logEvent("start_game", {
level: "10",
difficulty: "expert",
});
analytics.logEvent("select_content", {
content_type: "image",
content_id: "P12453",
items: [{ name: "Kittens" }],
});
}}>
Click me to start an event
</button>
<DashboardGridComponent />
</div>
);

View File

@@ -1,12 +1,29 @@
import React, { useEffect } from "react";
import ManageRootPageComponent from "./manage-root.page.component";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setBreadcrumbs } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import ManageRootPageComponent from "./manage-root.page.component";
export default function ManageRootPageContainer() {
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
});
export function ManageRootPageContainer({ setBreadcrumbs, bodyshop }) {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.manageroot");
}, [t]);
setBreadcrumbs([]);
}, [t, setBreadcrumbs]);
return <ManageRootPageComponent />;
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ManageRootPageContainer);

View File

@@ -20,6 +20,7 @@ import PrintCenterModalContainer from "../../components/print-center-modal/print
import { QUERY_STRIPE_ID } from "../../graphql/bodyshop.queries";
import { selectInstanceConflict } from "../../redux/user/user.selectors";
import "./manage.page.styles.scss";
import TestComponent from "../../components/_test/test.component";
const ManageRootPage = lazy(() =>
import("../manage-root/manage-root.page.container")
@@ -167,6 +168,11 @@ export function Manage({ match, conflict }) {
<PaymentModalContainer />
</Elements>
<Route exact path={`${match.path}`} component={ManageRootPage} />
<Route
exact
path={`${match.path}/ttt`}
component={TestComponent}
/>
<Route exact path={`${match.path}/jobs`} component={JobsPage} />
<Switch>
<Route

View File

@@ -305,6 +305,20 @@
"submittedsub": "Your input is highly appreciated."
}
},
"dashboard": {
"actions": {
"addcomponent": "Add Component"
},
"errors": {
"updatinglayout": "Error saving updated layout {{message}}"
},
"titles": {
"monthlyrevenuegraph": "Monthly Revenue Graph",
"productiondollars": "Total dollars in production",
"productionhours": "Total hours in production",
"projectedmonthlysales": "Projected Monthly Sales"
}
},
"documents": {
"actions": {
"delete": "Delete Selected Documents",

View File

@@ -305,6 +305,20 @@
"submittedsub": ""
}
},
"dashboard": {
"actions": {
"addcomponent": ""
},
"errors": {
"updatinglayout": ""
},
"titles": {
"monthlyrevenuegraph": "",
"productiondollars": "",
"productionhours": "",
"projectedmonthlysales": ""
}
},
"documents": {
"actions": {
"delete": "",

View File

@@ -305,6 +305,20 @@
"submittedsub": ""
}
},
"dashboard": {
"actions": {
"addcomponent": ""
},
"errors": {
"updatinglayout": ""
},
"titles": {
"monthlyrevenuegraph": "",
"productiondollars": "",
"productionhours": "",
"projectedmonthlysales": ""
}
},
"documents": {
"actions": {
"delete": "",

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."users" DROP COLUMN "dashboardlayout";
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."users" ADD COLUMN "dashboardlayout" jsonb NOT NULL
DEFAULT jsonb_build_array();
type: run_sql

View File

@@ -0,0 +1,24 @@
- args:
role: user
table:
name: users
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- authid
- created_at
- email
- fcmtokens
- updated_at
computed_fields: []
filter:
authid:
_eq: X-Hasura-User-Id
role: user
table:
name: users
schema: public
type: create_select_permission

View File

@@ -0,0 +1,25 @@
- args:
role: user
table:
name: users
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- authid
- created_at
- dashboardlayout
- email
- fcmtokens
- updated_at
computed_fields: []
filter:
authid:
_eq: X-Hasura-User-Id
role: user
table:
name: users
schema: public
type: create_select_permission

View File

@@ -0,0 +1,21 @@
- args:
role: user
table:
name: users
schema: public
type: drop_update_permission
- args:
permission:
columns:
- authid
- email
- fcmtokens
filter:
authid:
_eq: X-Hasura-User-Id
set: {}
role: user
table:
name: users
schema: public
type: create_update_permission

View File

@@ -0,0 +1,22 @@
- args:
role: user
table:
name: users
schema: public
type: drop_update_permission
- args:
permission:
columns:
- authid
- dashboardlayout
- email
- fcmtokens
filter:
authid:
_eq: X-Hasura-User-Id
set: {}
role: user
table:
name: users
schema: public
type: create_update_permission

View File

@@ -3684,6 +3684,7 @@ tables:
columns:
- authid
- created_at
- dashboardlayout
- email
- fcmtokens
- updated_at
@@ -3695,6 +3696,7 @@ tables:
permission:
columns:
- authid
- dashboardlayout
- email
- fcmtokens
filter: