├── .nvmrc ├── src ├── app │ ├── app.scss │ ├── app.html │ ├── app.vue │ └── app.ts ├── modules │ ├── main │ │ ├── main.scss │ │ ├── footer │ │ │ ├── footer.scss │ │ │ ├── footer.vue │ │ │ ├── footer.ts │ │ │ └── footer.html │ │ ├── header │ │ │ ├── header.scss │ │ │ ├── user │ │ │ │ ├── user.vue │ │ │ │ ├── user.ts │ │ │ │ ├── user.scss │ │ │ │ └── user.html │ │ │ ├── header.vue │ │ │ ├── messages │ │ │ │ ├── messages.vue │ │ │ │ ├── messages.ts │ │ │ │ ├── messages.scss │ │ │ │ └── messages.html │ │ │ ├── languages │ │ │ │ ├── languages.vue │ │ │ │ ├── languages.scss │ │ │ │ ├── languages.html │ │ │ │ └── languages.ts │ │ │ ├── notifications │ │ │ │ ├── notifications.vue │ │ │ │ ├── notifications.ts │ │ │ │ ├── notifications.scss │ │ │ │ └── notifications.html │ │ │ ├── header.html │ │ │ └── header.ts │ │ ├── main.vue │ │ ├── menu-sidebar │ │ │ ├── menu-sidebar.vue │ │ │ ├── menu-sidebar.scss │ │ │ ├── menu-sidebar.ts │ │ │ └── menu-sidebar.html │ │ ├── control-sidebar │ │ │ ├── control-sidebar.vue │ │ │ ├── control-sidebar.scss │ │ │ ├── control-sidebar.ts │ │ │ └── control-sidebar.html │ │ ├── main.html │ │ └── main.ts │ ├── forgot-password │ │ ├── forgot-password.scss │ │ ├── forgot-password.vue │ │ ├── forgot-password.ts │ │ └── forgot-password.html │ ├── recover-password │ │ ├── recover-password.scss │ │ ├── recover-password.vue │ │ ├── recover-password.ts │ │ └── recover-password.html │ ├── login │ │ ├── login.scss │ │ ├── login.vue │ │ ├── login.ts │ │ └── login.html │ └── register │ │ ├── register.scss │ │ ├── register.vue │ │ ├── register.ts │ │ └── register.html ├── pages │ ├── blank │ │ ├── blank.scss │ │ ├── blank.ts │ │ ├── blank.vue │ │ └── blank.html │ ├── dashboard │ │ ├── dashboard.scss │ │ ├── dashboard.ts │ │ ├── dashboard.vue │ │ └── dashboard.html │ ├── main-menu │ │ ├── main-menu.scss │ │ ├── sub-menu │ │ │ ├── sub-menu.scss │ │ │ ├── sub-menu.ts │ │ │ ├── sub-menu.vue │ │ │ └── sub-menu.html │ │ ├── main-menu.ts │ │ ├── main-menu.vue │ │ └── main-menu.html │ └── profile │ │ ├── profile.vue │ │ ├── profile.scss │ │ ├── profile.ts │ │ └── profile.html ├── components │ ├── menu-item │ │ ├── menu-item.scss │ │ ├── menu-item.vue │ │ ├── menu-item.html │ │ └── menu-item.ts │ ├── input │ │ ├── input.scss │ │ ├── input.vue │ │ ├── input.html │ │ └── input.ts │ └── sidebar-search │ │ ├── sidebar-search.vue │ │ ├── sidebar-search.scss │ │ ├── sidebar-search.html │ │ └── sidebar-search.ts ├── types │ └── user.ts ├── vite-env.d.ts ├── interfaces │ ├── payload.ts │ └── state.ts ├── shims-vue.d.ts ├── store │ ├── auth │ │ ├── actions.ts │ │ ├── getters.ts │ │ ├── mutations.ts │ │ └── index.ts │ ├── index.ts │ └── ui │ │ ├── getters.ts │ │ ├── index.ts │ │ ├── actions.ts │ │ └── mutations.ts ├── shims-vuex.d.ts ├── translation │ ├── index.ts │ ├── en.json │ ├── es.json │ └── tr.json ├── utils │ ├── axios.ts │ ├── helpers.ts │ └── themes.ts ├── firebase │ └── index.ts ├── index.scss ├── services │ └── auth.ts ├── main.ts └── router │ └── index.ts ├── .github ├── FUNDING.yml └── workflows │ └── deploy.yaml ├── .vercelignore ├── public ├── version.json ├── favicon.ico └── assets │ └── img │ ├── logo.png │ └── default-profile.png ├── .browserslistrc ├── .env.example ├── cypress.json ├── .firebaserc ├── summernote └── package.json ├── vercel.json ├── jest.config.js ├── .prettierrc.js ├── firebase.json ├── tests ├── e2e │ ├── .eslintrc.js │ ├── specs │ │ └── test.js │ ├── support │ │ ├── index.js │ │ └── commands.js │ └── plugins │ │ └── index.js └── unit │ ├── example.spec.ts │ ├── example.spec.js │ └── example.spec.js.map ├── .gitignore ├── .npmrc ├── vite.config.ts ├── tsconfig.json ├── LICENSE ├── .eslintrc.js ├── index.html ├── README.md ├── package.json └── CHANGELOG.md /.nvmrc: -------------------------------------------------------------------------------- 1 | v18.7.0 -------------------------------------------------------------------------------- /src/app/app.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/modules/main/main.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/blank/blank.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/modules/main/footer/footer.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/modules/main/header/header.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/dashboard/dashboard.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/main-menu/main-menu.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [erdkse] 2 | -------------------------------------------------------------------------------- /src/pages/main-menu/sub-menu/sub-menu.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vercelignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | 4 | .env -------------------------------------------------------------------------------- /public/version.json: -------------------------------------------------------------------------------- 1 | {"version" : "0.2.14"} 2 | -------------------------------------------------------------------------------- /src/modules/forgot-password/forgot-password.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/modules/recover-password/recover-password.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | VITE_NODE_ENV= 2 | VITE_FIREBASE_CONFIG= 3 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginsFile": "tests/e2e/plugins/index.js" 3 | } 4 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "admin-lte-3" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/components/menu-item/menu-item.scss: -------------------------------------------------------------------------------- 1 | .nav-item { 2 | cursor: pointer; 3 | } 4 | -------------------------------------------------------------------------------- /summernote/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "summernote", 3 | "version": "0.8.20" 4 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdkse/adminlte-3-vue/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/types/user.ts: -------------------------------------------------------------------------------- 1 | import {User} from 'firebase/auth'; 2 | 3 | export type IUser = User; 4 | -------------------------------------------------------------------------------- /src/app/app.html: -------------------------------------------------------------------------------- 1 | 2 |
Loading
3 | -------------------------------------------------------------------------------- /src/modules/login/login.scss: -------------------------------------------------------------------------------- 1 | pf-button { 2 | --pf-display: block; 3 | --pf-width: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /public/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdkse/adminlte-3-vue/HEAD/public/assets/img/logo.png -------------------------------------------------------------------------------- /src/modules/register/register.scss: -------------------------------------------------------------------------------- 1 | pf-button { 2 | --pf-display: block; 3 | --pf-width: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /public/assets/img/default-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdkse/adminlte-3-vue/HEAD/public/assets/img/default-profile.png -------------------------------------------------------------------------------- /src/components/input/input.scss: -------------------------------------------------------------------------------- 1 | .input-group-text { 2 | margin: inherit auto; 3 | padding: inherit auto; 4 | width: 48px; 5 | } 6 | -------------------------------------------------------------------------------- /src/interfaces/payload.ts: -------------------------------------------------------------------------------- 1 | import {IUser} from './user'; 2 | export interface IPayload { 3 | token: string; 4 | user: IUser; 5 | } 6 | -------------------------------------------------------------------------------- /src/pages/blank/blank.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | 3 | @Component({}) 4 | export default class Blank extends Vue {} 5 | -------------------------------------------------------------------------------- /src/app/app.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/pages/dashboard/dashboard.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | 3 | @Component({}) 4 | export default class Dashboard extends Vue {} 5 | -------------------------------------------------------------------------------- /src/pages/main-menu/main-menu.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | 3 | @Component({}) 4 | export default class MainMenu extends Vue {} 5 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "deploymentEnabled": false 4 | }, 5 | "rewrites": [{"source": "/(.*)", "destination": "/index.html"}] 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/main-menu/sub-menu/sub-menu.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | 3 | @Component({}) 4 | export default class SubMenu extends Vue {} 5 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@vue/cli-plugin-unit-jest/presets/typescript', 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /src/modules/main/main.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/pages/blank/blank.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/input/input.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/modules/login/login.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: 'none', 3 | tabWidth: 4, 4 | singleQuote: true, 5 | printWidth: 80, 6 | semi: true, 7 | bracketSpacing: false, 8 | }; 9 | -------------------------------------------------------------------------------- /src/modules/main/header/user/user.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/pages/profile/profile.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/modules/main/footer/footer.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/modules/main/header/header.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/modules/register/register.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/pages/dashboard/dashboard.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/pages/main-menu/main-menu.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/menu-item/menu-item.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/pages/main-menu/sub-menu/sub-menu.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "emulators": { 3 | "auth": { 4 | "port": 9099 5 | }, 6 | "ui": { 7 | "enabled": false 8 | }, 9 | "singleProjectMode": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/modules/main/header/messages/messages.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/modules/main/header/languages/languages.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type {DefineComponent} from 'vue'; 4 | const component: DefineComponent<{}, {}, any>; 5 | export default component; 6 | } 7 | -------------------------------------------------------------------------------- /src/modules/main/menu-sidebar/menu-sidebar.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/sidebar-search/sidebar-search.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/modules/forgot-password/forgot-password.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/modules/main/header/notifications/notifications.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/store/auth/actions.ts: -------------------------------------------------------------------------------- 1 | import {IUser} from '@/types/user'; 2 | 3 | export default { 4 | setCurrentUser: (context: any, payload: IUser): void => { 5 | context.commit('setCurrentUser', payload); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /tests/e2e/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['cypress'], 3 | env: { 4 | mocha: true, 5 | 'cypress/globals': true 6 | }, 7 | rules: { 8 | strict: 'off' 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /src/modules/main/control-sidebar/control-sidebar.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/modules/recover-password/recover-password.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/shims-vuex.d.ts: -------------------------------------------------------------------------------- 1 | import {Store} from 'vuex'; 2 | import {IState} from './interfaces/state'; 3 | 4 | declare module '@vue/runtime-core' { 5 | interface ComponentCustomProperties { 6 | $store: Store; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/store/auth/getters.ts: -------------------------------------------------------------------------------- 1 | import {IAuthState} from '@/interfaces/state'; 2 | import {IUser} from '@/types/user'; 3 | 4 | export default { 5 | currentUser: (state: IAuthState): IUser | undefined | null => 6 | state.currentUser 7 | }; 8 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import {createStore} from 'vuex'; 2 | import authModule from './auth'; 3 | import uiModule from './ui'; 4 | 5 | export default createStore({ 6 | modules: { 7 | auth: authModule, 8 | ui: uiModule 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /src/store/auth/mutations.ts: -------------------------------------------------------------------------------- 1 | import {IAuthState} from '@/interfaces/state'; 2 | import {IUser} from '@/types/user'; 3 | 4 | export default { 5 | setCurrentUser: (state: IAuthState, payload: IUser): void => { 6 | state.currentUser = payload; 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /src/modules/main/control-sidebar/control-sidebar.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | padding: 16px; 3 | padding-top: 73px; 4 | } 5 | 6 | pf-select { 7 | --pf-width: 100%; 8 | --pf-display: block; 9 | } 10 | 11 | pf-checkbox { 12 | --pf-display: block; 13 | } 14 | -------------------------------------------------------------------------------- /tests/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // https://docs.cypress.io/api/introduction/api.html 2 | 3 | describe('My First Test', () => { 4 | it('Visits the app root url', () => { 5 | cy.visit('/'); 6 | cy.contains('h1', 'Welcome to Your Vue.js + TypeScript App'); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/interfaces/state.ts: -------------------------------------------------------------------------------- 1 | import {IUser} from '@/types/user'; 2 | 3 | export interface IAuthState { 4 | currentUser?: IUser | null; 5 | } 6 | 7 | export interface IAuthModule { 8 | namespaced: boolean; 9 | state: IAuthState; 10 | mutations: any; 11 | actions: any; 12 | getters: any; 13 | } 14 | -------------------------------------------------------------------------------- /src/translation/index.ts: -------------------------------------------------------------------------------- 1 | import {createI18n} from 'vue-i18n'; 2 | 3 | import en from './en.json'; 4 | import es from './es.json'; 5 | import tr from './tr.json'; 6 | 7 | export const i18n = createI18n({ 8 | locale: 'en', 9 | messages: {en, es, tr}, 10 | fallbackLocale: 'en', 11 | legacy: false 12 | }); 13 | -------------------------------------------------------------------------------- /src/modules/main/header/notifications/notifications.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | import {Dropdown} from '@profabric/vue-components'; 3 | 4 | @Component({ 5 | name: 'notifications-dropdown', 6 | components: { 7 | 'pf-dropdown': Dropdown 8 | } 9 | }) 10 | export default class Notifications extends Vue {} 11 | -------------------------------------------------------------------------------- /src/pages/profile/profile.scss: -------------------------------------------------------------------------------- 1 | .user-img { 2 | --pf-border: 3px solid #adb5bd; 3 | --pf-padding: 3px; 4 | } 5 | 6 | .user-block { 7 | pf-image { 8 | --pf-border: 2px solid #adb5bd; 9 | --pf-padding: 2px; 10 | float: left; 11 | } 12 | } 13 | 14 | pf-button { 15 | margin-right: 0.25rem; 16 | --pf-width: 8rem; 17 | } -------------------------------------------------------------------------------- /src/modules/main/footer/footer.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | import {version} from '../../../../package.json'; 3 | import {DateTime} from 'luxon'; 4 | 5 | @Component({}) 6 | export default class Footer extends Vue { 7 | private version: string = version; 8 | private currentYear: string = DateTime.now().toFormat('y'); 9 | } 10 | -------------------------------------------------------------------------------- /src/modules/main/header/messages/messages.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | import {Dropdown, Image} from '@profabric/vue-components'; 3 | 4 | @Component({ 5 | name: 'messages-dropdown', 6 | components: { 7 | 'pf-dropdown': Dropdown, 8 | 'pf-image': Image 9 | } 10 | }) 11 | export default class Messages extends Vue {} 12 | -------------------------------------------------------------------------------- /src/utils/axios.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const intance = axios.create({ 4 | baseURL: `` 5 | }); 6 | 7 | intance.interceptors.request.use( 8 | (request) => request, 9 | (error) => Promise.reject(error) 10 | ); 11 | 12 | intance.interceptors.response.use( 13 | (response) => response, 14 | (error) => Promise.reject(error) 15 | ); 16 | 17 | export default intance; 18 | -------------------------------------------------------------------------------- /tests/unit/example.spec.ts: -------------------------------------------------------------------------------- 1 | import {shallowMount} from '@vue/test-utils'; 2 | import HelloWorld from '@/components/HelloWorld.vue'; 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('renders props.msg when passed', () => { 6 | const msg = 'new message'; 7 | const wrapper = shallowMount(HelloWorld, { 8 | props: {msg} 9 | }); 10 | expect(wrapper.text()).toMatch(msg); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/modules/main/header/messages/messages.scss: -------------------------------------------------------------------------------- 1 | pf-dropdown { 2 | border: none; 3 | width: 3rem; 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | --pf-dropdown-menu-min-width: 18rem; 8 | 9 | .dropdown-item { 10 | padding: 0.5rem 1rem; 11 | } 12 | 13 | .text-sm { 14 | margin-bottom: 0; 15 | } 16 | .dropdown-divider { 17 | margin: 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/modules/main/header/notifications/notifications.scss: -------------------------------------------------------------------------------- 1 | pf-dropdown { 2 | border: none; 3 | width: 3rem; 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | --pf-dropdown-menu-min-width: 18rem; 8 | 9 | .dropdown-item { 10 | padding: 0.5rem 1rem; 11 | } 12 | 13 | .text-sm { 14 | margin-bottom: 0; 15 | } 16 | .dropdown-divider { 17 | margin: 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | /tests/e2e/videos/ 6 | /tests/e2e/screenshots/ 7 | 8 | # local env files 9 | .env.local 10 | .env.*.local 11 | 12 | # Log files 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | pnpm-debug.log* 17 | 18 | # Editor directories and files 19 | .idea 20 | .vscode 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | 27 | .env 28 | 29 | firebase-debug.log 30 | ui-debug.log 31 | -------------------------------------------------------------------------------- /src/store/auth/index.ts: -------------------------------------------------------------------------------- 1 | import mutations from './mutations'; 2 | import actions from './actions'; 3 | import getters from './getters'; 4 | import {IAuthModule} from '@/interfaces/state'; 5 | 6 | const authModule: IAuthModule = { 7 | namespaced: true, 8 | state: { 9 | authentication: undefined, 10 | currentUser: undefined 11 | }, 12 | mutations, 13 | actions, 14 | getters 15 | }; 16 | 17 | export default authModule; 18 | -------------------------------------------------------------------------------- /src/modules/main/header/languages/languages.scss: -------------------------------------------------------------------------------- 1 | pf-dropdown { 2 | border: none; 3 | width: 3rem; 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | --pf-dropdown-menu-min-width: 10rem; 8 | 9 | .dropdown-item { 10 | padding: 0.5rem 1rem; 11 | cursor: pointer; 12 | } 13 | 14 | .text-sm { 15 | margin-bottom: 0; 16 | } 17 | .dropdown-divider { 18 | margin: 0; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/store/ui/getters.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | darkModeSelected: (state: any): boolean => state.darkMode, 3 | menuSidebarCollapsed: (state: any): boolean => state.menuSidebarCollapsed, 4 | controlSidebarCollapsed: (state: any): boolean => 5 | state.controlSidebarCollapsed, 6 | screenSize: (state: any): boolean => state.screenSize, 7 | navbarVariant: (state: any): string => state.navbarVariant, 8 | sidebarSkin: (state: any): string => state.sidebarSkin 9 | }; 10 | -------------------------------------------------------------------------------- /src/components/input/input.html: -------------------------------------------------------------------------------- 1 |
2 | 10 |
11 |
12 | 13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /src/pages/profile/profile.ts: -------------------------------------------------------------------------------- 1 | import {Button, Image} from '@profabric/vue-components'; 2 | import {Component, Vue} from 'vue-facing-decorator'; 3 | 4 | @Component({ 5 | name: 'app-profile', 6 | components: { 7 | 'pf-image': Image, 8 | 'pf-button': Button 9 | } 10 | }) 11 | export default class Profile extends Vue { 12 | private activeTab = 'ACTIVITY'; 13 | 14 | private setActiveTab(tab: string): void { 15 | this.activeTab = tab; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/main/menu-sidebar/menu-sidebar.scss: -------------------------------------------------------------------------------- 1 | .brand-image { 2 | float: left !important; 3 | line-height: 0.8 !important; 4 | margin: -1px 8px 0 6px !important; 5 | opacity: 0.8 !important; 6 | --pf-box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 7 | 0 6px 6px rgba(0, 0, 0, 0.23) !important; 8 | } 9 | 10 | .img-circle { 11 | --pf-box-shadow: 0 3px 6px #00000029, 0 3px 6px #0000003b !important; 12 | } 13 | 14 | .form-inline { 15 | width: 100%; 16 | justify-content: center; 17 | } 18 | -------------------------------------------------------------------------------- /src/components/input/input.ts: -------------------------------------------------------------------------------- 1 | import {Component, Prop, Vue} from 'vue-facing-decorator'; 2 | 3 | @Component({ 4 | name: 'app-input', 5 | emits: ['update:modelValue'] 6 | }) 7 | export default class Input extends Vue { 8 | @Prop() modelValue: string; 9 | @Prop() icon: string; 10 | @Prop() type: string; 11 | @Prop() placeholder: string; 12 | @Prop() autocomplete: string; 13 | 14 | public onValueChange(event: any) { 15 | this.$emit('update:modelValue', event.target.value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/main/footer/footer.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | {{ $t("labels.copyright") }} © {{currentYear}} 4 | 5 |  erdkse.com 6 | 7 | . 8 | 9 |
10 | {{ $t("labels.version") }} 11 | : {{version}} 12 |
13 |
14 | -------------------------------------------------------------------------------- /src/modules/main/main.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 |
7 | 8 | 9 | 14 |
15 | 16 |
Loading
17 | -------------------------------------------------------------------------------- /src/modules/forgot-password/forgot-password.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | 3 | @Component({}) 4 | export default class ForgotPassword extends Vue { 5 | private appElement: HTMLElement | null = null; 6 | 7 | public mounted(): void { 8 | this.appElement = document.getElementById('app') as HTMLElement; 9 | this.appElement.classList.add('login-page'); 10 | } 11 | 12 | public unmounted(): void { 13 | (this.appElement as HTMLElement).classList.remove('login-page'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/firebase/index.ts: -------------------------------------------------------------------------------- 1 | import {FirebaseOptions, initializeApp} from 'firebase/app'; 2 | import {connectAuthEmulator, getAuth} from 'firebase/auth'; 3 | 4 | let {VITE_FIREBASE_CONFIG, PROD} = import.meta.env; 5 | 6 | const firebaseConfig: FirebaseOptions = VITE_FIREBASE_CONFIG 7 | ? JSON.parse(VITE_FIREBASE_CONFIG) 8 | : {apiKey: 'MOCK_KEY'}; 9 | 10 | const app = initializeApp(firebaseConfig); 11 | 12 | export const firebaseAuth = getAuth(app); 13 | 14 | if (!PROD) { 15 | connectAuthEmulator(firebaseAuth, 'http://localhost:9099'); 16 | } 17 | -------------------------------------------------------------------------------- /tests/unit/example.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable prettier/prettier */ 2 | /* eslint-disable no-var */ 3 | import { shallowMount } from '@vue/test-utils'; 4 | import HelloWorld from '@/components/HelloWorld.vue'; 5 | describe('HelloWorld.vue', function () { 6 | it('renders props.msg when passed', function () { 7 | var msg = 'new message'; 8 | var wrapper = shallowMount(HelloWorld, { 9 | props: { msg: msg } 10 | }); 11 | expect(wrapper.text()).toMatch(msg); 12 | }); 13 | }); 14 | //# sourceMappingURL=example.spec.js.map -------------------------------------------------------------------------------- /tests/unit/example.spec.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "file": "example.spec.js", 4 | "sourceRoot": "", 5 | "sources": [ 6 | "example.spec.ts" 7 | ], 8 | "names": [], 9 | "mappings": "AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAC;AAC7C,OAAO,UAAU,MAAM,6BAA6B,CAAC;AAErD,QAAQ,CAAC,gBAAgB,EAAE;IACvB,EAAE,CAAC,+BAA+B,EAAE;QAChC,IAAM,GAAG,GAAG,aAAa,CAAC;QAC1B,IAAM,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE;YACrC,KAAK,EAAE,EAAC,GAAG,KAAA,EAAC;SACf,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC" 10 | } 11 | -------------------------------------------------------------------------------- /src/modules/main/header/languages/languages.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 16 |
17 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # Supress wall of text on 'npm install' 2 | audit = false 3 | fund = false 4 | update-notifier = true 5 | # Lock versions of node and npm, when looking up engines. 6 | node-version = 18.7.0 7 | npm-version = 8.15.0 8 | # Default options for package-lock - package-lock=true package-lock-only=false 9 | # If set to false, then ignore package-lock.json files when installing. This will also prevent writing package-lock.json if save is true. 10 | # When package package-locks are disabled, automatic pruning of extraneous modules will also be disabled. 11 | package-lock = true 12 | package-lock-only = false 13 | legacy-peer-deps = true 14 | -------------------------------------------------------------------------------- /src/store/ui/index.ts: -------------------------------------------------------------------------------- 1 | import mutations from './mutations'; 2 | import actions from './actions'; 3 | import getters from './getters'; 4 | import {calculateWindowSize} from '@/utils/helpers'; 5 | 6 | const uiModule = { 7 | namespaced: true, 8 | state: { 9 | darkMode: false, 10 | navbarVariant: 'navbar-light', 11 | sidebarSkin: 'sidebar-dark-primary', 12 | menuSidebarCollapsed: false, 13 | controlSidebarCollapsed: true, 14 | screenSize: calculateWindowSize(window.innerWidth) 15 | }, 16 | mutations, 17 | actions, 18 | getters 19 | }; 20 | 21 | export default uiModule; 22 | -------------------------------------------------------------------------------- /src/components/sidebar-search/sidebar-search.scss: -------------------------------------------------------------------------------- 1 | pf-dropdown { 2 | width: calc(100% - 12px); 3 | --pf-dropdown-border: none; 4 | --pf-dropdown-menu-min-width: 100%; 5 | --pf-dropdown-menu-margin-top: 0px; 6 | 7 | .menu { 8 | background-color: #454d55; 9 | } 10 | 11 | .dropdown-item { 12 | padding: 0.5rem 1rem; 13 | } 14 | } 15 | 16 | .nothing-found { 17 | color: #c2c7d0; 18 | padding: 0.25rem 0.5rem; 19 | } 20 | 21 | .list-group { 22 | .list-group-item { 23 | padding: 0.5rem 0.75rem; 24 | cursor: pointer; 25 | } 26 | 27 | .search-path { 28 | font-size: 80%; 29 | color: #adb5bd; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/index.scss: -------------------------------------------------------------------------------- 1 | @import url('https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css'); 2 | @import url('https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.3.0/css/flag-icon.min.css'); 3 | @import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700'); 4 | 5 | @import 'admin-lte/plugins/fontawesome-free/css/all.min.css'; 6 | @import 'admin-lte/plugins/icheck-bootstrap/icheck-bootstrap.min.css'; 7 | @import 'admin-lte/dist/css/adminlte.min.css'; 8 | @import 'vue-toastification/dist/index.css'; 9 | 10 | #app { 11 | width: 100vw; 12 | height: 100vh; 13 | } 14 | 15 | .main-header .navbar-nav .nav-item .nav-link { 16 | background: transparent; 17 | border: none; 18 | outline: none; 19 | cursor: pointer; 20 | } 21 | -------------------------------------------------------------------------------- /tests/e2e/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /src/store/ui/actions.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | toggleDarkMode: (context: any): any => { 3 | context.commit('toggleDarkMode'); 4 | }, 5 | toggleMenuSidebar: (context: any): any => { 6 | context.commit('toggleMenuSidebar'); 7 | }, 8 | toggleControlSidebar: (context: any): any => { 9 | context.commit('toggleControlSidebar'); 10 | }, 11 | setWindowSize: (context: any, payload: string): void => { 12 | context.commit('setWindowSize', payload); 13 | }, 14 | setNavbarVariant: (context: any, payload: string): void => { 15 | context.commit('setNavbarVariant', payload); 16 | }, 17 | setSidebarSkin: (context: any, payload: string): void => { 18 | context.commit('setSidebarSkin', payload); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import {defineConfig, loadEnv} from 'vite'; 2 | import vue from '@vitejs/plugin-vue'; 3 | import path from 'path'; 4 | import 'dotenv/config'; 5 | 6 | export default ({mode}) => { 7 | process.env = {...process.env, ...loadEnv(mode, process.cwd())}; 8 | 9 | const {VITE_NODE_ENV} = process.env; 10 | 11 | return defineConfig({ 12 | mode: VITE_NODE_ENV, 13 | plugins: [vue()], 14 | resolve: { 15 | alias: { 16 | '@': path.resolve(__dirname, './src'), 17 | '@store': path.resolve(__dirname, './src/store'), 18 | '@components': path.resolve(__dirname, './src/components'), 19 | '@modules': path.resolve(__dirname, './src/modules'), 20 | '@pages': path.resolve(__dirname, './src/pages') 21 | } 22 | } 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /src/modules/recover-password/recover-password.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | import Input from '@/components/input/input.vue'; 3 | import {useToast} from 'vue-toastification'; 4 | import {Button} from '@profabric/vue-components'; 5 | 6 | @Component({ 7 | components: { 8 | 'app-input': Input, 9 | 'pf-button': Button 10 | } 11 | }) 12 | export default class RecoverPassword extends Vue { 13 | private appElement: HTMLElement | null = null; 14 | public password: string = ''; 15 | public confirmPassword: string = ''; 16 | private toast = useToast(); 17 | 18 | public mounted(): void { 19 | this.appElement = document.getElementById('app') as HTMLElement; 20 | this.appElement.classList.add('login-page'); 21 | } 22 | 23 | public unmounted(): void { 24 | (this.appElement as HTMLElement).classList.remove('login-page'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/e2e/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This is will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /src/components/menu-item/menu-item.html: -------------------------------------------------------------------------------- 1 | 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": false, 14 | "baseUrl": ".", 15 | "types": ["webpack-env", "jest"], 16 | "paths": { 17 | "@/*": ["src/*"] 18 | }, 19 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 20 | "resolveJsonModule": true, 21 | "strictNullChecks": false, 22 | "noEmit": true 23 | }, 24 | "include": [ 25 | "src/**/*.ts", 26 | "src/**/*.tsx", 27 | "src/**/*.vue", 28 | "tests/**/*.ts", 29 | "tests/**/*.tsx" 30 | ], 31 | "exclude": ["node_modules"] 32 | } 33 | -------------------------------------------------------------------------------- /tests/e2e/plugins/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable arrow-body-style */ 2 | // https://docs.cypress.io/guides/guides/plugins-guide.html 3 | 4 | // if you need a custom webpack configuration you can uncomment the following import 5 | // and then use the `file:preprocessor` event 6 | // as explained in the cypress docs 7 | // https://docs.cypress.io/api/plugins/preprocessors-api.html#Examples 8 | 9 | // /* eslint-disable import/no-extraneous-dependencies, global-require */ 10 | // const webpack = require('@cypress/webpack-preprocessor') 11 | 12 | module.exports = (on, config) => { 13 | // on('file:preprocessor', webpack({ 14 | // webpackOptions: require('@vue/cli-service/webpack.config'), 15 | // watchOptions: {} 16 | // })) 17 | 18 | return Object.assign({}, config, { 19 | fixturesFolder: 'tests/e2e/fixtures', 20 | integrationFolder: 'tests/e2e/specs', 21 | screenshotsFolder: 'tests/e2e/screenshots', 22 | videosFolder: 'tests/e2e/videos', 23 | supportFile: 'tests/e2e/support/index.js' 24 | }); 25 | }; 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Erdi Köse 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | extends: [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/typescript/recommended', 10 | '@vue/prettier' 11 | ], 12 | parserOptions: { 13 | ecmaVersion: 2020 14 | }, 15 | rules: { 16 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 17 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 18 | 'no-useless-catch': 'off', 19 | '@typescript-eslint/no-explicit-any': 'off', 20 | '@typescript-eslint/explicit-module-boundary-types': 'off', 21 | '@typescript-eslint/no-inferrable-types': 'off', 22 | 'vue/script-setup-uses-vars': 'off', 23 | 'vue/multi-word-component-names': 'off' 24 | }, 25 | overrides: [ 26 | { 27 | files: [ 28 | '**/__tests__/*.{j,t}s?(x)', 29 | '**/tests/unit/**/*.spec.{j,t}s?(x)' 30 | ], 31 | env: { 32 | jest: true 33 | } 34 | } 35 | ] 36 | }; 37 | -------------------------------------------------------------------------------- /src/utils/helpers.ts: -------------------------------------------------------------------------------- 1 | export const calculateWindowSize = (windowWidth: number): string => { 2 | if (windowWidth >= 1200) { 3 | return 'lg'; 4 | } 5 | if (windowWidth >= 992) { 6 | return 'md'; 7 | } 8 | if (windowWidth >= 768) { 9 | return 'sm'; 10 | } 11 | return 'xs'; 12 | }; 13 | 14 | export const sleep = (time: number) => 15 | new Promise((res) => setTimeout(res, time)); 16 | 17 | export const setWindowClass = (classList: string) => { 18 | const window: HTMLElement | null = 19 | document && document.getElementById('root'); 20 | if (window) { 21 | (window as any).classList = classList; 22 | } 23 | }; 24 | export const addWindowClass = (classList: string) => { 25 | const window: HTMLElement | null = 26 | document && document.getElementById('root'); 27 | if (window) { 28 | window.classList.add(classList); 29 | } 30 | }; 31 | 32 | export const removeWindowClass = (classList: string) => { 33 | const window: HTMLElement | null = 34 | document && document.getElementById('root'); 35 | if (window) { 36 | window.classList.remove(classList); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yaml: -------------------------------------------------------------------------------- 1 | name: Production Tag Deployment 2 | env: 3 | VITE_NODE_ENV: ${{ secrets.VITE_NODE_ENV }} 4 | VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} 5 | VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} 6 | VITE_FIREBASE_CONFIG: ${{ secrets.VITE_FIREBASE_CONFIG }} 7 | VITE_GA_ID: ${{ secrets.VITE_GA_ID }} 8 | on: 9 | workflow_dispatch: 10 | push: 11 | tags: 12 | - '*' 13 | jobs: 14 | Deploy-Production: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Install Vercel CLI 19 | run: npm install --global vercel@latest 20 | - name: Pull Vercel Environment Information 21 | run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} 22 | - name: Build Project Artifacts 23 | run: | 24 | export NODE_OPTIONS="--max_old_space_size=4096" 25 | vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} 26 | - name: Deploy Project Artifacts to Vercel 27 | run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} 28 | -------------------------------------------------------------------------------- /src/services/auth.ts: -------------------------------------------------------------------------------- 1 | import {firebaseAuth} from '@/firebase'; 2 | import {createUserWithEmailAndPassword} from '@firebase/auth'; 3 | import {signInWithEmailAndPassword, signInWithPopup} from 'firebase/auth'; 4 | import {GoogleAuthProvider} from 'firebase/auth'; 5 | 6 | const provider = new GoogleAuthProvider(); 7 | 8 | export const registerWithEmail = async (email: string, password: string) => { 9 | try { 10 | const result = await createUserWithEmailAndPassword( 11 | firebaseAuth, 12 | email, 13 | password 14 | ); 15 | return result; 16 | } catch (error) { 17 | throw error; 18 | } 19 | }; 20 | 21 | export const loginWithEmail = async (email: string, password: string) => { 22 | try { 23 | const result = await signInWithEmailAndPassword( 24 | firebaseAuth, 25 | email, 26 | password 27 | ); 28 | return result; 29 | } catch (error) { 30 | throw error; 31 | } 32 | }; 33 | 34 | export const signInByGoogle = async () => { 35 | try { 36 | return await signInWithPopup(firebaseAuth, provider); 37 | } catch (error) { 38 | throw error; 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | AdminLTE 3 10 | 22 | 23 | 24 | 30 |
31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/modules/main/header/user/user.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | import {DateTime} from 'luxon'; 3 | import {Dropdown, Image} from '@profabric/vue-components'; 4 | import {firebaseAuth} from '@/firebase'; 5 | 6 | @Component({ 7 | name: 'user-dropdown', 8 | components: { 9 | 'pf-dropdown': Dropdown, 10 | 'pf-image': Image 11 | } 12 | }) 13 | export default class User extends Vue { 14 | get authentication(): any { 15 | return this.$store.getters['auth/currentUser']; 16 | } 17 | 18 | async logout() { 19 | try { 20 | await firebaseAuth.signOut(); 21 | this.$store.dispatch('auth/setCurrentUser', undefined); 22 | this.$router.replace('/login'); 23 | } catch (error) { 24 | console.log(error); 25 | } 26 | } 27 | 28 | get readableCreatedAtDate() { 29 | if ( 30 | this.authentication && 31 | this.authentication.metadata && 32 | this.authentication.metadata.createdAt 33 | ) { 34 | return DateTime.fromMillis( 35 | +this.authentication.metadata.createdAt 36 | ).toFormat('dd LLLL yyyy'); 37 | } 38 | return ''; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/modules/main/header/header.html: -------------------------------------------------------------------------------- 1 | 2 | 37 | -------------------------------------------------------------------------------- /src/modules/main/header/header.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-empty-function */ 2 | /* eslint-disable @typescript-eslint/no-unused-vars */ 3 | import {Component, Vue} from 'vue-facing-decorator'; 4 | import Messages from './messages/messages.vue'; 5 | import Notifications from './notifications/notifications.vue'; 6 | import Languages from './languages/languages.vue'; 7 | import User from './user/user.vue'; 8 | 9 | @Component({ 10 | components: { 11 | 'messages-dropdown': Messages, 12 | 'notifications-dropdown': Notifications, 13 | 'languages-dropdown': Languages, 14 | 'user-dropdown': User 15 | } 16 | }) 17 | export default class Header extends Vue { 18 | private headerElement: HTMLElement | null = null; 19 | public async mounted(): Promise { 20 | this.headerElement = document.getElementById( 21 | 'main-header' 22 | ) as HTMLElement; 23 | } 24 | 25 | public onToggleMenuSidebar(): void { 26 | this.$store.dispatch('ui/toggleMenuSidebar'); 27 | } 28 | 29 | public onToggleControlSidebar(): void { 30 | this.$store.dispatch('ui/toggleControlSidebar'); 31 | } 32 | 33 | get darkModeSelected() { 34 | return this.$store.getters['ui/darkModeSelected']; 35 | } 36 | 37 | get navbarVariant() { 38 | return this.$store.getters['ui/navbarVariant']; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue'; 2 | import App from './app/app.vue'; 3 | import router from './router'; 4 | import store from './store'; 5 | import {i18n} from './translation'; 6 | 7 | import {library} from '@fortawesome/fontawesome-svg-core'; 8 | import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'; 9 | 10 | import Toast, {PluginOptions} from 'vue-toastification'; 11 | import {ProfabricComponents} from '@profabric/vue-components'; 12 | import VueGtag from 'vue-gtag'; 13 | 14 | import './index.scss'; 15 | import {faEnvelope, faLock} from '@fortawesome/free-solid-svg-icons'; 16 | 17 | const {VITE_NODE_ENV, VITE_GA_ID} = import.meta.env; 18 | 19 | library.add(faEnvelope, faLock); 20 | 21 | const options: PluginOptions = { 22 | timeout: 3000, 23 | closeOnClick: true, 24 | pauseOnFocusLoss: true, 25 | pauseOnHover: true, 26 | draggable: true, 27 | draggablePercent: 0.6, 28 | showCloseButtonOnHover: false, 29 | hideProgressBar: false, 30 | closeButton: 'button', 31 | icon: true, 32 | rtl: false 33 | }; 34 | 35 | const app = createApp(App); 36 | app.component('font-awesome-icon', FontAwesomeIcon) 37 | .use(store) 38 | .use(router) 39 | .use(Toast, options) 40 | .use(i18n as any) 41 | .use(ProfabricComponents); 42 | 43 | if (VITE_NODE_ENV === 'production' && VITE_GA_ID) { 44 | app.use( 45 | VueGtag, 46 | { 47 | config: {id: VITE_GA_ID} 48 | }, 49 | router 50 | ); 51 | } 52 | 53 | app.mount('#app'); 54 | -------------------------------------------------------------------------------- /src/modules/main/menu-sidebar/menu-sidebar.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | import MenuItem from '@/components/menu-item/menu-item.vue'; 3 | import {Image} from '@profabric/vue-components'; 4 | import SidebarSearch from '@/components/sidebar-search/sidebar-search.vue'; 5 | import {i18n} from '@/translation'; 6 | import {IUser} from '@/types/user'; 7 | import {toRaw} from 'vue'; 8 | 9 | @Component({ 10 | name: 'app-menu-sidebar', 11 | components: { 12 | 'app-menu-item': MenuItem, 13 | 'app-sidebar-search': SidebarSearch, 14 | 'pf-image': Image 15 | } 16 | }) 17 | export default class MenuSidebar extends Vue { 18 | menu = MENU; 19 | 20 | get currentUser(): IUser | undefined { 21 | const user = this.$store.getters['auth/currentUser']; 22 | return user; 23 | } 24 | 25 | get sidebarSkin() { 26 | return this.$store.getters['ui/sidebarSkin']; 27 | } 28 | } 29 | 30 | export const MENU = [ 31 | { 32 | name: i18n.global.t('labels.dashboard'), 33 | path: '/' 34 | }, 35 | { 36 | name: i18n.global.t('labels.blank'), 37 | path: '/blank' 38 | }, 39 | { 40 | name: i18n.global.t('labels.mainMenu'), 41 | children: [ 42 | { 43 | name: i18n.global.t('labels.subMenu'), 44 | path: '/sub-menu-1' 45 | }, 46 | 47 | { 48 | name: i18n.global.t('labels.blank'), 49 | path: '/sub-menu-2' 50 | } 51 | ] 52 | } 53 | ]; 54 | -------------------------------------------------------------------------------- /src/modules/main/header/notifications/notifications.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 15 5 |
6 | 38 |
39 | -------------------------------------------------------------------------------- /src/store/ui/mutations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NAVBAR_DARK_VARIANTS, 3 | NAVBAR_LIGHT_VARIANTS, 4 | SIDEBAR_DARK_SKINS, 5 | SIDEBAR_LIGHT_SKINS 6 | } from '@/utils/themes'; 7 | 8 | export default { 9 | toggleDarkMode: (state: any): void => { 10 | state.darkMode = !state.darkMode; 11 | if (state.darkMode) { 12 | state.navbarVariant = NAVBAR_DARK_VARIANTS[0].value; 13 | state.sidebarSkin = SIDEBAR_DARK_SKINS[0].value; 14 | } else { 15 | state.navbarVariant = NAVBAR_LIGHT_VARIANTS[0].value; 16 | state.sidebarSkin = SIDEBAR_LIGHT_SKINS[0].value; 17 | } 18 | }, 19 | toggleMenuSidebar: (state: any): void => { 20 | state.menuSidebarCollapsed = !state.menuSidebarCollapsed; 21 | }, 22 | toggleControlSidebar: (state: any): void => { 23 | state.controlSidebarCollapsed = !state.controlSidebarCollapsed; 24 | }, 25 | setWindowSize: (state: any, payload: string): void => { 26 | state.screenSize = payload; 27 | }, 28 | setNavbarVariant: (state: any, payload: string): void => { 29 | if (state.darkMode) { 30 | state.navbarVariant = payload || NAVBAR_DARK_VARIANTS[0].value; 31 | } else { 32 | state.navbarVariant = payload || NAVBAR_LIGHT_VARIANTS[0].value; 33 | } 34 | }, 35 | setSidebarSkin: (state: any, payload: string): void => { 36 | if (state.darkMode) { 37 | state.sidebarSkin = payload || SIDEBAR_DARK_SKINS[0].value; 38 | } else { 39 | state.sidebarSkin = payload || SIDEBAR_LIGHT_SKINS[0].value; 40 | } 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /src/modules/main/control-sidebar/control-sidebar.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-facing-decorator'; 2 | import { 3 | Option, 4 | NAVBAR_DARK_VARIANTS, 5 | NAVBAR_LIGHT_VARIANTS, 6 | SIDEBAR_DARK_SKINS, 7 | SIDEBAR_LIGHT_SKINS 8 | } from '@/utils/themes'; 9 | import {Checkbox, Select} from '@profabric/vue-components'; 10 | 11 | @Component({ 12 | name: 'app-control-sidebar', 13 | components: { 14 | 'pf-checkbox': Checkbox, 15 | 'pf-select': Select 16 | } 17 | }) 18 | export default class ControlSidebar extends Vue { 19 | private navbarLightVariants: Array