diff --git a/app/Http/Controllers/Api/V1/Settings/AccountController.php b/app/Http/Controllers/Api/V1/Settings/AccountController.php
new file mode 100644
index 0000000..4ad1d34
--- /dev/null
+++ b/app/Http/Controllers/Api/V1/Settings/AccountController.php
@@ -0,0 +1,76 @@
+user = auth()->guard('api')->user();
+ }
+
+ /**
+ * Update User's login credentials
+ *
+ * @param Illuminate\Http\Request $request
+ *
+ * @return Illuminate\Http\JsonResponse
+ */
+ public function updateCredentials(Request $request) : JsonResponse
+ {
+ $request->validate([
+ 'username' =>
+ "required|string|unique:users,username,{$this->user->id},id,deleted_at,NULL",
+ 'email' =>
+ "required|email|unique:users,email,{$this->user->id},id,deleted_at,NULL"
+ ]);
+
+ $this->user->username = $request->input('username');
+ $this->user->email = $request->input('email');
+ $this->user->update();
+
+ return response()->json($this->user);
+ }
+
+ /**
+ * Update User's password
+ *
+ * @param Illuminate\Http\Request $request
+ *
+ * @return Illuminate\Http\JsonResponse
+ */
+ public function updatePassword(Request $request) : JsonResponse
+ {
+ $request->validate([
+ 'old_password' => 'required|string',
+ 'password' => 'required|string|confirmed|min:8|pwned:100'
+ ]);
+
+ if (! Hash::check($request->input('old_password'), $this->user->password)) {
+ throw ValidationException::withMessages([
+ 'old_password' => [trans('auth.password_mismatch')]
+ ]);
+
+ return response()->json('Password was not Changed!', 422);
+ }
+
+ $this->user->password = bcrypt($request->input('password'));
+ $this->user->update();
+
+ return response()->json('Password Changed!');
+ }
+}
diff --git a/app/Http/Controllers/Api/V1/Settings/ProfileController.php b/app/Http/Controllers/Api/V1/Settings/ProfileController.php
new file mode 100644
index 0000000..c1c7beb
--- /dev/null
+++ b/app/Http/Controllers/Api/V1/Settings/ProfileController.php
@@ -0,0 +1,40 @@
+guard('api')->user();
+
+ $request->validate([
+ 'firstname' => 'required|string|max:255',
+ 'lastname' => 'required|string|max:255',
+
+ 'gender' => 'nullable|in:female,male',
+ 'birthdate' =>
+ 'nullable|date:Y-m-d|before:'.now()->subYear(10)->format('Y-m-d'),
+ 'address' => 'nullable|string|max:510',
+ ]);
+
+ $attributes = $request->all();
+ unset($attributes['auth_token']);
+
+ $user->fill($attributes);
+ $user->update();
+
+ return response()->json($user);
+ }
+}
diff --git a/resources/js/config/locale.js b/resources/js/config/locale.js
index 7182278..626ab71 100644
--- a/resources/js/config/locale.js
+++ b/resources/js/config/locale.js
@@ -3,6 +3,7 @@ export default {
'en.actions': require('../../lang/en/actions.php'),
'en.navigation': require('../../lang/en/navigation.php'),
'en.resources': require('../../lang/en/resources.php'),
+ 'en.settings': require('../../lang/en/settings.php'),
'en.table': require('../../lang/en/table.php'),
'en.validation': require('../../lang/en/validation.php'),
@@ -10,6 +11,7 @@ export default {
'fil.actions': require('../../lang/fil/actions.php'),
'fil.navigation': require('../../lang/fil/navigation.php'),
'fil.resources': require('../../lang/fil/resources.php'),
+ 'fil.settings': require('../../lang/fil/settings.php'),
'fil.table': require('../../lang/fil/table.php'),
'fil.validation': require('../../lang/fil/validation.php'),
};
diff --git a/resources/js/routers/backoffice.js b/resources/js/routers/backoffice.js
index 9f3290b..74e4ef6 100644
--- a/resources/js/routers/backoffice.js
+++ b/resources/js/routers/backoffice.js
@@ -1,4 +1,5 @@
import { Home } from '../views/__backoffice';
+import * as Settings from '../views/__backoffice/settings';
import * as Users from '../views/__backoffice/users';
export default [
@@ -8,6 +9,18 @@ export default [
component: Home,
},
+ {
+ name: 'settings.profile',
+ path: '/settings/profile',
+ component: Settings.Profile,
+ },
+
+ {
+ name: 'settings.account',
+ path: '/settings/account',
+ component: Settings.Account,
+ },
+
{
name: 'users.index',
path: '/users',
diff --git a/resources/js/views/__backoffice/Home.js b/resources/js/views/__backoffice/Home.js
index 2f9c6ed..85c37a3 100755
--- a/resources/js/views/__backoffice/Home.js
+++ b/resources/js/views/__backoffice/Home.js
@@ -28,7 +28,6 @@ class Home extends Component {
pageTitle={Lang.get('navigation.dashboard')}
primaryAction={primaryAction}
tabs={tabs}
- breadcrumbs={[]}
>
There is no place like home
diff --git a/resources/js/views/__backoffice/layouts/Clean.js b/resources/js/views/__backoffice/layouts/Clean.js
new file mode 100644
index 0000000..4ca4947
--- /dev/null
+++ b/resources/js/views/__backoffice/layouts/Clean.js
@@ -0,0 +1,165 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+
+import {
+ CircularProgress,
+ CssBaseline,
+ Grid,
+ Hidden,
+ withStyles,
+} from '@material-ui/core';
+import classNames from 'classnames';
+
+import { Snackbar } from '../../../ui';
+import { LinearDeterminate } from '../../../ui/Loaders';
+import { Footer, Header, Sidebar } from '../partials';
+
+function Clean(props) {
+ const { classes, ...other } = props;
+ const { history, loading, message } = props;
+
+ const [drawerOpen, setDrawer] = useState(false);
+ const [localeMenuOpen, setLocaleMenu] = useState(false);
+ const [accountMenuOpen, setAccountMenu] = useState(false);
+
+ const sidebarProps = Object.assign(other, {
+ navigate: path => history.push(path),
+ PaperProps: { style: { width: drawerWidth } },
+ open: drawerOpen,
+ onClose: () => setDrawer(!drawerOpen),
+ });
+
+ const renderLoading = (
+
+
+
+
+
+ );
+
+ return (
+ <>
+ {loading && }
+
+
+
+
+
+
+
+ setDrawer(!drawerOpen)}
+ onLocaleMenuToggle={() =>
+ setLocaleMenu(!localeMenuOpen)
+ }
+ onAccountMenuToggle={() =>
+ setAccountMenu(!accountMenuOpen)
+ }
+ />
+
+
+ {loading ? renderLoading : props.children}
+
+
+
+
+
+
+ {message && message.hasOwnProperty('type') && (
+
+ )}
+ >
+ );
+}
+
+Clean.propTypes = {
+ classes: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
+ location: PropTypes.object.isRequired,
+ match: PropTypes.object.isRequired,
+ pageProps: PropTypes.object.isRequired,
+
+ pageTitle: PropTypes.string,
+ loading: PropTypes.bool,
+ message: PropTypes.object,
+};
+
+Clean.defaultProps = {
+ pageTitle: '',
+ loading: false,
+ message: {},
+};
+
+const drawerWidth = 256;
+
+const styles = theme => ({
+ loader: {
+ zIndex: 9999,
+ },
+
+ root: {
+ display: 'flex',
+ position: 'relative',
+ minHeight: '100vh',
+ maxWidth: '100%',
+ },
+
+ drawer: {
+ drawer: {
+ width: drawerWidth,
+ flexShrink: 0,
+ },
+ },
+
+ contentWrapper: {
+ flex: 1,
+ display: 'flex',
+ flexDirection: 'column',
+ overflowX: 'scroll',
+ },
+
+ content: {
+ flex: 1,
+ padding: `0 ${theme.spacing.unit}px`,
+ marginBottom: 75,
+ marginLeft: 0,
+ [theme.breakpoints.up('sm')]: {
+ marginBottom: 50,
+ padding: `${theme.spacing.unit}px ${theme.spacing.unit * 3}px`,
+ },
+ },
+
+ contentShift: {
+ [theme.breakpoints.up('sm')]: {
+ transition: theme.transitions.create('margin', {
+ easing: theme.transitions.easing.easeOut,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
+ marginLeft: drawerWidth,
+ },
+ },
+});
+
+export default withStyles(styles)(Clean);
diff --git a/resources/js/views/__backoffice/layouts/Master.js b/resources/js/views/__backoffice/layouts/Master.js
index aabca7e..9ffe168 100755
--- a/resources/js/views/__backoffice/layouts/Master.js
+++ b/resources/js/views/__backoffice/layouts/Master.js
@@ -22,18 +22,34 @@ import * as StringUtils from '../../../utils/String';
import * as UrlUtils from '../../../utils/URL';
import { Snackbar, Modal } from '../../../ui';
import { LinearDeterminate } from '../../../ui/Loaders';
-import { Header, Sidebar } from '../partials';
+import { Footer, Header, Sidebar } from '../partials';
class Master extends Component {
state = {
mobileOpen: false,
localeMenuOpen: false,
- localeMenuEl: null,
accountMenuOpen: false,
- accountMenuEl: null,
message: {},
};
+ /**
+ * Toggles Locale Menu
+ *
+ * @return {undefined}
+ */
+ handleLocaleMenuToggled = () => {
+ this.handleNavLinkMenuToggled('localeMenuOpen');
+ };
+
+ /**
+ * Toggles Account Menu
+ *
+ * @return {undefined}
+ */
+ handleAccountMenuToggled = () => {
+ this.handleNavLinkMenuToggled('accountMenuOpen');
+ };
+
/**
* Event listener that is triggered when the a nav link menu is clicked.
*
@@ -96,7 +112,7 @@ class Master extends Component {
}
render() {
- const { classes, ...childProps } = this.props;
+ const { classes, showBreadcrumbs, ...other } = this.props;
const {
children,
@@ -130,6 +146,76 @@ class Master extends Component {
);
+ const renderBreadcrumbs = (
+
+
+
+ {segments.length > 0 ? (
+ (
+
+ )}
+ className={classes.breadcrumbItem}
+ >
+
+
+ ) : (
+
+ )}
+
+ {segments.map((segment, key) => {
+ if (key + 1 === segments.length) {
+ return (
+
+ {StringUtils._uppercaseFirst(segment)}
+
+ );
+ }
+
+ return (
+ (
+
+ )}
+ className={classes.breadcrumbItem}
+ >
+ {StringUtils._uppercaseFirst(segment)}
+
+ );
+ })}
+
+
+
+ );
+
return (
<>
{loading && }
@@ -140,7 +226,7 @@ class Master extends Component {