Initial commit

This commit is contained in:
Nguyen Ha Khue
2023-04-20 12:49:16 +07:00
commit 7fe4d98325
1020 changed files with 99355 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+1577
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
<excludeFolder url="file://$MODULE_DIR$/dist" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+6
View File
@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/backend.iml" filepath="$PROJECT_DIR$/.idea/backend.iml" />
</modules>
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+27
View File
@@ -0,0 +1,27 @@
# MultikartBackend
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.1.1.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
+136
View File
@@ -0,0 +1,136 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"multikart-backend": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"allowedCommonJsDependencies": [
"mousetrap",
"hammerjs",
"@ks89/angular-modal-gallery",
"hammerjs",
"rxjs/internal/operators",
"lodash",
"chartist",
"chart.js",
"clone-deep"
],
"outputPath": "dist/multikart-backend",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.scss",
"src/assets/scss/style.scss",
"node_modules/font-awesome/scss/font-awesome.scss"
],
"scripts": [],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "multikart-backend:build"
},
"configurations": {
"production": {
"browserTarget": "multikart-backend:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "multikart-backend:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "multikart-backend:serve"
},
"configurations": {
"production": {
"devServerTarget": "multikart-backend:serve:production"
}
}
}
}
}
},
"cli": {
"analytics": false
}
}
+32
View File
@@ -0,0 +1,32 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
+23
View File
@@ -0,0 +1,23 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('multikart-backend app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
+11
View File
@@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl) as Promise<unknown>;
}
getTitleText(): Promise<string> {
return element(by.css('app-root .content span')).getText() as Promise<string>;
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
+395
View File
@@ -0,0 +1,395 @@
#!/usr/bin/env bash
{ # this ensures the entire script is downloaded #
nvm_has() {
type "$1" > /dev/null 2>&1
}
nvm_install_dir() {
printf %s "${NVM_DIR:-"$HOME/.nvm"}"
}
nvm_latest_version() {
echo "v0.33.8"
}
nvm_profile_is_bash_or_zsh() {
local TEST_PROFILE
TEST_PROFILE="${1-}"
case "${TEST_PROFILE-}" in
*"/.bashrc" | *"/.bash_profile" | *"/.zshrc")
return
;;
*)
return 1
;;
esac
}
#
# Outputs the location to NVM depending on:
# * The availability of $NVM_SOURCE
# * The method used ("script" or "git" in the script, defaults to "git")
# NVM_SOURCE always takes precedence unless the method is "script-nvm-exec"
#
nvm_source() {
local NVM_METHOD
NVM_METHOD="$1"
local NVM_SOURCE_URL
NVM_SOURCE_URL="$NVM_SOURCE"
if [ "_$NVM_METHOD" = "_script-nvm-exec" ]; then
NVM_SOURCE_URL="https://raw.githubusercontent.com/creationix/nvm/$(nvm_latest_version)/nvm-exec"
elif [ "_$NVM_METHOD" = "_script-nvm-bash-completion" ]; then
NVM_SOURCE_URL="https://raw.githubusercontent.com/creationix/nvm/$(nvm_latest_version)/bash_completion"
elif [ -z "$NVM_SOURCE_URL" ]; then
if [ "_$NVM_METHOD" = "_script" ]; then
NVM_SOURCE_URL="https://raw.githubusercontent.com/creationix/nvm/$(nvm_latest_version)/nvm.sh"
elif [ "_$NVM_METHOD" = "_git" ] || [ -z "$NVM_METHOD" ]; then
NVM_SOURCE_URL="https://github.com/creationix/nvm.git"
else
echo >&2 "Unexpected value \"$NVM_METHOD\" for \$NVM_METHOD"
return 1
fi
fi
echo "$NVM_SOURCE_URL"
}
#
# Node.js version to install
#
nvm_node_version() {
echo "$NODE_VERSION"
}
nvm_download() {
if nvm_has "curl"; then
curl --compressed -q "$@"
elif nvm_has "wget"; then
# Emulate curl with wget
ARGS=$(echo "$*" | command sed -e 's/--progress-bar /--progress=bar /' \
-e 's/-L //' \
-e 's/--compressed //' \
-e 's/-I /--server-response /' \
-e 's/-s /-q /' \
-e 's/-o /-O /' \
-e 's/-C - /-c /')
# shellcheck disable=SC2086
eval wget $ARGS
fi
}
install_nvm_from_git() {
local INSTALL_DIR
INSTALL_DIR="$(nvm_install_dir)"
if [ -d "$INSTALL_DIR/.git" ]; then
echo "=> nvm is already installed in $INSTALL_DIR, trying to update using git"
command printf "\r=> "
command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" fetch origin tag "$(nvm_latest_version)" --depth=1 2> /dev/null || {
echo >&2 "Failed to update nvm, run 'git fetch' in $INSTALL_DIR yourself."
exit 1
}
else
# Cloning to $INSTALL_DIR
echo "=> Downloading nvm from git to '$INSTALL_DIR'"
command printf "\r=> "
mkdir -p "${INSTALL_DIR}"
if [ "$(ls -A "${INSTALL_DIR}")" ]; then
command git init "${INSTALL_DIR}" || {
echo >&2 'Failed to initialize nvm repo. Please report this!'
exit 2
}
command git --git-dir="${INSTALL_DIR}/.git" remote add origin "$(nvm_source)" 2> /dev/null \
|| command git --git-dir="${INSTALL_DIR}/.git" remote set-url origin "$(nvm_source)" || {
echo >&2 'Failed to add remote "origin" (or set the URL). Please report this!'
exit 2
}
command git --git-dir="${INSTALL_DIR}/.git" fetch origin tag "$(nvm_latest_version)" --depth=1 || {
echo >&2 'Failed to fetch origin with tags. Please report this!'
exit 2
}
else
command git clone "$(nvm_source)" -b "$(nvm_latest_version)" --depth=1 "${INSTALL_DIR}" || {
echo >&2 'Failed to clone nvm repo. Please report this!'
exit 2
}
fi
fi
command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" checkout -f --quiet "$(nvm_latest_version)"
if [ ! -z "$(command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" show-ref refs/heads/master)" ]; then
if command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" branch --quiet 2>/dev/null; then
command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" branch --quiet -D master >/dev/null 2>&1
else
echo >&2 "Your version of git is out of date. Please update it!"
command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" branch -D master >/dev/null 2>&1
fi
fi
echo "=> Compressing and cleaning up git repository"
if ! command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" reflog expire --expire=now --all; then
echo >&2 "Your version of git is out of date. Please update it!"
fi
if ! command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" gc --auto --aggressive --prune=now ; then
echo >&2 "Your version of git is out of date. Please update it!"
fi
return
}
#
# Automatically install Node.js
#
nvm_install_node() {
local NODE_VERSION_LOCAL
NODE_VERSION_LOCAL="$(nvm_node_version)"
if [ -z "$NODE_VERSION_LOCAL" ]; then
return 0
fi
echo "=> Installing Node.js version $NODE_VERSION_LOCAL"
nvm install "$NODE_VERSION_LOCAL"
local CURRENT_NVM_NODE
CURRENT_NVM_NODE="$(nvm_version current)"
if [ "$(nvm_version "$NODE_VERSION_LOCAL")" == "$CURRENT_NVM_NODE" ]; then
echo "=> Node.js version $NODE_VERSION_LOCAL has been successfully installed"
else
echo >&2 "Failed to install Node.js $NODE_VERSION_LOCAL"
fi
}
install_nvm_as_script() {
local INSTALL_DIR
INSTALL_DIR="$(nvm_install_dir)"
local NVM_SOURCE_LOCAL
NVM_SOURCE_LOCAL="$(nvm_source script)"
local NVM_EXEC_SOURCE
NVM_EXEC_SOURCE="$(nvm_source script-nvm-exec)"
local NVM_BASH_COMPLETION_SOURCE
NVM_BASH_COMPLETION_SOURCE="$(nvm_source script-nvm-bash-completion)"
# Downloading to $INSTALL_DIR
mkdir -p "$INSTALL_DIR"
if [ -f "$INSTALL_DIR/nvm.sh" ]; then
echo "=> nvm is already installed in $INSTALL_DIR, trying to update the script"
else
echo "=> Downloading nvm as script to '$INSTALL_DIR'"
fi
nvm_download -s "$NVM_SOURCE_LOCAL" -o "$INSTALL_DIR/nvm.sh" || {
echo >&2 "Failed to download '$NVM_SOURCE_LOCAL'"
return 1
} &
nvm_download -s "$NVM_EXEC_SOURCE" -o "$INSTALL_DIR/nvm-exec" || {
echo >&2 "Failed to download '$NVM_EXEC_SOURCE'"
return 2
} &
nvm_download -s "$NVM_BASH_COMPLETION_SOURCE" -o "$INSTALL_DIR/bash_completion" || {
echo >&2 "Failed to download '$NVM_BASH_COMPLETION_SOURCE'"
return 2
} &
for job in $(jobs -p | sort)
do
wait "$job" || return $?
done
chmod a+x "$INSTALL_DIR/nvm-exec" || {
echo >&2 "Failed to mark '$INSTALL_DIR/nvm-exec' as executable"
return 3
}
}
nvm_try_profile() {
if [ -z "${1-}" ] || [ ! -f "${1}" ]; then
return 1
fi
echo "${1}"
}
#
# Detect profile file if not specified as environment variable
# (eg: PROFILE=~/.myprofile)
# The echo'ed path is guaranteed to be an existing file
# Otherwise, an empty string is returned
#
nvm_detect_profile() {
if [ -n "${PROFILE}" ] && [ -f "${PROFILE}" ]; then
echo "${PROFILE}"
return
fi
local DETECTED_PROFILE
DETECTED_PROFILE=''
local SHELLTYPE
SHELLTYPE="$(basename "/$SHELL")"
if [ "$SHELLTYPE" = "bash" ]; then
if [ -f "$HOME/.bashrc" ]; then
DETECTED_PROFILE="$HOME/.bashrc"
elif [ -f "$HOME/.bash_profile" ]; then
DETECTED_PROFILE="$HOME/.bash_profile"
fi
elif [ "$SHELLTYPE" = "zsh" ]; then
DETECTED_PROFILE="$HOME/.zshrc"
fi
if [ -z "$DETECTED_PROFILE" ]; then
for EACH_PROFILE in ".profile" ".bashrc" ".bash_profile" ".zshrc"
do
if DETECTED_PROFILE="$(nvm_try_profile "${HOME}/${EACH_PROFILE}")"; then
break
fi
done
fi
if [ ! -z "$DETECTED_PROFILE" ]; then
echo "$DETECTED_PROFILE"
fi
}
#
# Check whether the user has any globally-installed npm modules in their system
# Node, and warn them if so.
#
nvm_check_global_modules() {
command -v npm >/dev/null 2>&1 || return 0
local NPM_VERSION
NPM_VERSION="$(npm --version)"
NPM_VERSION="${NPM_VERSION:--1}"
[ "${NPM_VERSION%%[!-0-9]*}" -gt 0 ] || return 0
local NPM_GLOBAL_MODULES
NPM_GLOBAL_MODULES="$(
npm list -g --depth=0 |
command sed -e '/ npm@/d' -e '/ (empty)$/d'
)"
local MODULE_COUNT
MODULE_COUNT="$(
command printf %s\\n "$NPM_GLOBAL_MODULES" |
command sed -ne '1!p' | # Remove the first line
wc -l | tr -d ' ' # Count entries
)"
if [ "${MODULE_COUNT}" != '0' ]; then
# shellcheck disable=SC2016
echo '=> You currently have modules installed globally with `npm`. These will no'
# shellcheck disable=SC2016
echo '=> longer be linked to the active version of Node when you install a new node'
# shellcheck disable=SC2016
echo '=> with `nvm`; and they may (depending on how you construct your `$PATH`)'
# shellcheck disable=SC2016
echo '=> override the binaries of modules installed with `nvm`:'
echo
command printf %s\\n "$NPM_GLOBAL_MODULES"
echo '=> If you wish to uninstall them at a later point (or re-install them under your'
# shellcheck disable=SC2016
echo '=> `nvm` Nodes), you can remove them from the system Node as follows:'
echo
echo ' $ nvm use system'
echo ' $ npm uninstall -g a_module'
echo
fi
}
nvm_do_install() {
if [ -z "${METHOD}" ]; then
# Autodetect install method
if nvm_has git; then
install_nvm_from_git
elif nvm_has nvm_download; then
install_nvm_as_script
else
echo >&2 'You need git, curl, or wget to install nvm'
exit 1
fi
elif [ "${METHOD}" = 'git' ]; then
if ! nvm_has git; then
echo >&2 "You need git to install nvm"
exit 1
fi
install_nvm_from_git
elif [ "${METHOD}" = 'script' ]; then
if ! nvm_has nvm_download; then
echo >&2 "You need curl or wget to install nvm"
exit 1
fi
install_nvm_as_script
fi
echo
local NVM_PROFILE
NVM_PROFILE="$(nvm_detect_profile)"
local PROFILE_INSTALL_DIR
PROFILE_INSTALL_DIR="$(nvm_install_dir| sed "s:^$HOME:\$HOME:")"
SOURCE_STR="\nexport NVM_DIR=\"${PROFILE_INSTALL_DIR}\"\n[ -s \"\$NVM_DIR/nvm.sh\" ] && \\. \"\$NVM_DIR/nvm.sh\" # This loads nvm\n"
COMPLETION_STR="[ -s \"\$NVM_DIR/bash_completion\" ] && \\. \"\$NVM_DIR/bash_completion\" # This loads nvm bash_completion\n"
BASH_OR_ZSH=false
if [ -z "${NVM_PROFILE-}" ] ; then
local TRIED_PROFILE
if [ -n "${PROFILE}" ]; then
TRIED_PROFILE="${NVM_PROFILE} (as defined in \$PROFILE), "
fi
echo "=> Profile not found. Tried ${TRIED_PROFILE-}~/.bashrc, ~/.bash_profile, ~/.zshrc, and ~/.profile."
echo "=> Create one of them and run this script again"
echo " OR"
echo "=> Append the following lines to the correct file yourself:"
command printf "${SOURCE_STR}"
else
if nvm_profile_is_bash_or_zsh "${NVM_PROFILE-}"; then
BASH_OR_ZSH=true
fi
if ! command grep -qc '/nvm.sh' "$NVM_PROFILE"; then
echo "=> Appending nvm source string to $NVM_PROFILE"
command printf "${SOURCE_STR}" >> "$NVM_PROFILE"
else
echo "=> nvm source string already in ${NVM_PROFILE}"
fi
# shellcheck disable=SC2016
if ${BASH_OR_ZSH} && ! command grep -qc '$NVM_DIR/bash_completion' "$NVM_PROFILE"; then
echo "=> Appending bash_completion source string to $NVM_PROFILE"
command printf "$COMPLETION_STR" >> "$NVM_PROFILE"
else
echo "=> bash_completion source string already in ${NVM_PROFILE}"
fi
fi
if ${BASH_OR_ZSH} && [ -z "${NVM_PROFILE-}" ] ; then
echo "=> Please also append the following lines to the if you are using bash/zsh shell:"
command printf "${COMPLETION_STR}"
fi
# Source nvm
# shellcheck source=/dev/null
\. "$(nvm_install_dir)/nvm.sh"
nvm_check_global_modules
nvm_install_node
nvm_reset
echo "=> Close and reopen your terminal to start using nvm or run the following to use it now:"
command printf "${SOURCE_STR}"
if ${BASH_OR_ZSH} ; then
command printf "${COMPLETION_STR}"
fi
}
#
# Unsets the various functions defined
# during the execution of the install script
#
nvm_reset() {
unset -f nvm_has nvm_install_dir nvm_latest_version nvm_profile_is_bash_or_zsh \
nvm_source nvm_node_version nvm_download install_nvm_from_git nvm_install_node \
install_nvm_as_script nvm_try_profile nvm_detect_profile nvm_check_global_modules \
nvm_do_install nvm_reset
}
[ "_$NVM_ENV" = "_testing" ] || nvm_do_install
} # this ensures the entire script is downloaded #
+32
View File
@@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/multikart-backend'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
+352
View File
@@ -0,0 +1,352 @@
#!/bin/bash
# Discussion, issues and change requests at:
# https://github.com/nodesource/distributions
#
# Script to install the NodeSource Node.js 8.x LTS Carbon repo onto a
# Debian or Ubuntu system.
#
# Run as root or insert `sudo -E` before `bash`:
#
# curl -sL https://deb.nodesource.com/setup_8.x | bash -
# or
# wget -qO- https://deb.nodesource.com/setup_8.x | bash -
#
# CONTRIBUTIONS TO THIS SCRIPT
#
# This script is built from a template in
# https://github.com/nodesource/distributions/tree/master/deb/src
# please don't submit pull requests against the built scripts.
#
export DEBIAN_FRONTEND=noninteractive
SCRSUFFIX="_8.x"
NODENAME="Node.js 8.x LTS Carbon"
NODEREPO="node_8.x"
NODEPKG="nodejs"
print_status() {
echo
echo "## $1"
echo
}
if test -t 1; then # if terminal
ncolors=$(which tput > /dev/null && tput colors) # supports color
if test -n "$ncolors" && test $ncolors -ge 8; then
termcols=$(tput cols)
bold="$(tput bold)"
underline="$(tput smul)"
standout="$(tput smso)"
normal="$(tput sgr0)"
black="$(tput setaf 0)"
red="$(tput setaf 1)"
green="$(tput setaf 2)"
yellow="$(tput setaf 3)"
blue="$(tput setaf 4)"
magenta="$(tput setaf 5)"
cyan="$(tput setaf 6)"
white="$(tput setaf 7)"
fi
fi
print_bold() {
title="$1"
text="$2"
echo
echo "${red}================================================================================${normal}"
echo "${red}================================================================================${normal}"
echo
echo -e " ${bold}${yellow}${title}${normal}"
echo
echo -en " ${text}"
echo
echo "${red}================================================================================${normal}"
echo "${red}================================================================================${normal}"
}
bail() {
echo 'Error executing command, exiting'
exit 1
}
exec_cmd_nobail() {
echo "+ $1"
bash -c "$1"
}
exec_cmd() {
exec_cmd_nobail "$1" || bail
}
node_deprecation_warning() {
if [[ "X${NODENAME}" == "Xio.js 1.x" ||
"X${NODENAME}" == "Xio.js 2.x" ||
"X${NODENAME}" == "Xio.js 3.x" ||
"X${NODENAME}" == "XNode.js 0.10" ||
"X${NODENAME}" == "XNode.js 0.12" ||
"X${NODENAME}" == "XNode.js 4.x LTS Argon" ||
"X${NODENAME}" == "XNode.js 5.x" ||
"X${NODENAME}" == "XNode.js 6.x LTS Boron" ||
"X${NODENAME}" == "XNode.js 7.x" ||
"X${NODENAME}" == "XNode.js 8.x LTS Carbon" ||
"X${NODENAME}" == "XNode.js 9.x" ||
"X${NODENAME}" == "XNode.js 10.x" ||
"X${NODENAME}" == "XNode.js 11.x" ||
"X${NODENAME}" == "XNode.js 13.x" ||
"X${NODENAME}" == "XNode.js 15.x" ]]; then
print_bold \
" DEPRECATION WARNING " "\
${bold}${NODENAME} is no longer actively supported!${normal}
${bold}You will not receive security or critical stability updates${normal} for this version.
You should migrate to a supported version of Node.js as soon as possible.
Use the installation script that corresponds to the version of Node.js you
wish to install. e.g.
* ${green}https://deb.nodesource.com/setup_12.x — Node.js 12 LTS \"Erbium\"${normal}
* ${green}https://deb.nodesource.com/setup_14.x — Node.js 14 LTS \"Fermium\"${normal} (recommended)
* ${green}https://deb.nodesource.com/setup_16.x — Node.js 16 \"Gallium\"${normal}
Please see ${bold}https://github.com/nodejs/Release${normal} for details about which
version may be appropriate for you.
The ${bold}NodeSource${normal} Node.js distributions repository contains
information both about supported versions of Node.js and supported Linux
distributions. To learn more about usage, see the repository:
${bold}https://github.com/nodesource/distributions${normal}
"
echo
echo "Continuing in 20 seconds ..."
echo
sleep 20
fi
}
script_deprecation_warning() {
if [ "X${SCRSUFFIX}" == "X" ]; then
print_bold \
" SCRIPT DEPRECATION WARNING " "\
This script, located at ${bold}https://deb.nodesource.com/setup${normal}, used to
install Node.js 0.10, is deprecated and will eventually be made inactive.
You should use the script that corresponds to the version of Node.js you
wish to install. e.g.
* ${green}https://deb.nodesource.com/setup_12.x — Node.js 12 LTS \"Erbium\"${normal}
* ${green}https://deb.nodesource.com/setup_14.x — Node.js 14 LTS \"Fermium\"${normal} (recommended)
* ${green}https://deb.nodesource.com/setup_16.x — Node.js 16 \"Gallium\"${normal}
Please see ${bold}https://github.com/nodejs/Release${normal} for details about which
version may be appropriate for you.
The ${bold}NodeSource${normal} Node.js Linux distributions GitHub repository contains
information about which versions of Node.js and which Linux distributions
are supported and how to use the install scripts.
${bold}https://github.com/nodesource/distributions${normal}
"
echo
echo "Continuing in 20 seconds (press Ctrl-C to abort) ..."
echo
sleep 20
fi
}
setup() {
script_deprecation_warning
node_deprecation_warning
print_status "Installing the NodeSource ${NODENAME} repo..."
if $(uname -m | grep -Eq ^armv6); then
print_status "You appear to be running on ARMv6 hardware. Unfortunately this is not currently supported by the NodeSource Linux distributions. Please use the 'linux-armv6l' binary tarballs available directly from nodejs.org for Node.js 4 and later."
exit 1
fi
PRE_INSTALL_PKGS=""
# Check that HTTPS transport is available to APT
# (Check snaked from: https://get.docker.io/ubuntu/)
if [ ! -e /usr/lib/apt/methods/https ]; then
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} apt-transport-https"
fi
if [ ! -x /usr/bin/lsb_release ]; then
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} lsb-release"
fi
if [ ! -x /usr/bin/curl ] && [ ! -x /usr/bin/wget ]; then
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} curl"
fi
# Used by apt-key to add new keys
if [ ! -x /usr/bin/gpg ]; then
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} gnupg"
fi
# Populating Cache
print_status "Populating apt-get cache..."
exec_cmd 'apt-get update'
if [ "X${PRE_INSTALL_PKGS}" != "X" ]; then
print_status "Installing packages required for setup:${PRE_INSTALL_PKGS}..."
# This next command needs to be redirected to /dev/null or the script will bork
# in some environments
exec_cmd "apt-get install -y${PRE_INSTALL_PKGS} > /dev/null 2>&1"
fi
IS_PRERELEASE=$(lsb_release -d | grep 'Ubuntu .*development' >& /dev/null; echo $?)
if [[ $IS_PRERELEASE -eq 0 ]]; then
print_status "Your distribution, identified as \"$(lsb_release -d -s)\", is a pre-release version of Ubuntu. NodeSource does not maintain official support for Ubuntu versions until they are formally released. You can try using the manual installation instructions available at https://github.com/nodesource/distributions and use the latest supported Ubuntu version name as the distribution identifier, although this is not guaranteed to work."
exit 1
fi
DISTRO=$(lsb_release -c -s)
check_alt() {
if [ "X${DISTRO}" == "X${2}" ]; then
echo
echo "## You seem to be using ${1} version ${DISTRO}."
echo "## This maps to ${3} \"${4}\"... Adjusting for you..."
DISTRO="${4}"
fi
}
check_alt "SolydXK" "solydxk-9" "Debian" "stretch"
check_alt "Kali" "sana" "Debian" "jessie"
check_alt "Kali" "kali-rolling" "Debian" "bullseye"
check_alt "Sparky Linux" "Tyche" "Debian" "stretch"
check_alt "Sparky Linux" "Nibiru" "Debian" "buster"
check_alt "MX Linux 17" "Horizon" "Debian" "stretch"
check_alt "MX Linux 18" "Continuum" "Debian" "stretch"
check_alt "MX Linux 19" "patito feo" "Debian" "buster"
check_alt "Linux Mint" "maya" "Ubuntu" "precise"
check_alt "Linux Mint" "qiana" "Ubuntu" "trusty"
check_alt "Linux Mint" "rafaela" "Ubuntu" "trusty"
check_alt "Linux Mint" "rebecca" "Ubuntu" "trusty"
check_alt "Linux Mint" "rosa" "Ubuntu" "trusty"
check_alt "Linux Mint" "sarah" "Ubuntu" "xenial"
check_alt "Linux Mint" "serena" "Ubuntu" "xenial"
check_alt "Linux Mint" "sonya" "Ubuntu" "xenial"
check_alt "Linux Mint" "sylvia" "Ubuntu" "xenial"
check_alt "Linux Mint" "tara" "Ubuntu" "bionic"
check_alt "Linux Mint" "tessa" "Ubuntu" "bionic"
check_alt "Linux Mint" "tina" "Ubuntu" "bionic"
check_alt "Linux Mint" "tricia" "Ubuntu" "bionic"
check_alt "Linux Mint" "ulyana" "Ubuntu" "focal"
check_alt "Linux Mint" "ulyssa" "Ubuntu" "focal"
check_alt "Linux Mint" "uma" "Ubuntu" "focal"
check_alt "LMDE" "betsy" "Debian" "jessie"
check_alt "LMDE" "cindy" "Debian" "stretch"
check_alt "LMDE" "debbie" "Debian" "buster"
check_alt "elementaryOS" "luna" "Ubuntu" "precise"
check_alt "elementaryOS" "freya" "Ubuntu" "trusty"
check_alt "elementaryOS" "loki" "Ubuntu" "xenial"
check_alt "elementaryOS" "juno" "Ubuntu" "bionic"
check_alt "elementaryOS" "hera" "Ubuntu" "bionic"
check_alt "elementaryOS" "odin" "Ubuntu" "focal"
check_alt "Trisquel" "toutatis" "Ubuntu" "precise"
check_alt "Trisquel" "belenos" "Ubuntu" "trusty"
check_alt "Trisquel" "flidas" "Ubuntu" "xenial"
check_alt "Trisquel" "etiona" "Ubuntu" "bionic"
check_alt "Uruk GNU/Linux" "lugalbanda" "Ubuntu" "xenial"
check_alt "BOSS" "anokha" "Debian" "wheezy"
check_alt "BOSS" "anoop" "Debian" "jessie"
check_alt "BOSS" "drishti" "Debian" "stretch"
check_alt "BOSS" "unnati" "Debian" "buster"
check_alt "bunsenlabs" "bunsen-hydrogen" "Debian" "jessie"
check_alt "bunsenlabs" "helium" "Debian" "stretch"
check_alt "bunsenlabs" "lithium" "Debian" "buster"
check_alt "Tanglu" "chromodoris" "Debian" "jessie"
check_alt "PureOS" "green" "Debian" "sid"
check_alt "PureOS" "amber" "Debian" "buster"
check_alt "Devuan" "jessie" "Debian" "jessie"
check_alt "Devuan" "ascii" "Debian" "stretch"
check_alt "Devuan" "beowulf" "Debian" "buster"
check_alt "Devuan" "ceres" "Debian" "sid"
check_alt "Deepin" "panda" "Debian" "sid"
check_alt "Deepin" "unstable" "Debian" "sid"
check_alt "Deepin" "stable" "Debian" "buster"
check_alt "Pardus" "onyedi" "Debian" "stretch"
check_alt "Liquid Lemur" "lemur-3" "Debian" "stretch"
check_alt "Astra Linux" "orel" "Debian" "stretch"
check_alt "Ubilinux" "dolcetto" "Debian" "stretch"
if [ "X${DISTRO}" == "Xdebian" ]; then
print_status "Unknown Debian-based distribution, checking /etc/debian_version..."
NEWDISTRO=$([ -e /etc/debian_version ] && cut -d/ -f1 < /etc/debian_version)
if [ "X${NEWDISTRO}" == "X" ]; then
print_status "Could not determine distribution from /etc/debian_version..."
else
DISTRO=$NEWDISTRO
print_status "Found \"${DISTRO}\" in /etc/debian_version..."
fi
fi
print_status "Confirming \"${DISTRO}\" is supported..."
if [ -x /usr/bin/curl ]; then
exec_cmd_nobail "curl -sLf -o /dev/null 'https://deb.nodesource.com/${NODEREPO}/dists/${DISTRO}/Release'"
RC=$?
else
exec_cmd_nobail "wget -qO /dev/null -o /dev/null 'https://deb.nodesource.com/${NODEREPO}/dists/${DISTRO}/Release'"
RC=$?
fi
if [[ $RC != 0 ]]; then
print_status "Your distribution, identified as \"${DISTRO}\", is not currently supported, please contact NodeSource at https://github.com/nodesource/distributions/issues if you think this is incorrect or would like your distribution to be considered for support"
exit 1
fi
if [ -f "/etc/apt/sources.list.d/chris-lea-node_js-$DISTRO.list" ]; then
print_status 'Removing Launchpad PPA Repository for NodeJS...'
exec_cmd_nobail 'add-apt-repository -y -r ppa:chris-lea/node.js'
exec_cmd "rm -f /etc/apt/sources.list.d/chris-lea-node_js-${DISTRO}.list"
fi
print_status 'Adding the NodeSource signing key to your keyring...'
keyring='/usr/share/keyrings'
node_key_url="https://deb.nodesource.com/gpgkey/nodesource.gpg.key"
local_node_key="$keyring/nodesource.gpg"
if [ -x /usr/bin/curl ]; then
exec_cmd "curl -s $node_key_url | gpg --dearmor | tee $local_node_key >/dev/null"
else
exec_cmd "wget -q -O - $node_key_url | gpg --dearmor | tee $local_node_key >/dev/null"
fi
print_status "Creating apt sources list file for the NodeSource ${NODENAME} repo..."
exec_cmd "echo 'deb [signed-by=$local_node_key] https://deb.nodesource.com/${NODEREPO} ${DISTRO} main' > /etc/apt/sources.list.d/nodesource.list"
exec_cmd "echo 'deb-src [signed-by=$local_node_key] https://deb.nodesource.com/${NODEREPO} ${DISTRO} main' >> /etc/apt/sources.list.d/nodesource.list"
print_status 'Running `apt-get update` for you...'
exec_cmd 'apt-get update'
yarn_site='https://dl.yarnpkg.com/debian'
yarn_key_url="$yarn_site/pubkey.gpg"
local_yarn_key="$keyring/yarnkey.gpg"
print_status """Run \`${bold}sudo apt-get install -y ${NODEPKG}${normal}\` to install ${NODENAME} and npm
## You may also need development tools to build native addons:
sudo apt-get install gcc g++ make
## To install the Yarn package manager, run:
curl -sL $yarn_key_url | gpg --dearmor | sudo tee $local_yarn_key >/dev/null
echo \"deb [signed-by=$local_yarn_key] $yarn_site stable main\" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn
"""
}
## Defer setup until we have the complete script
setup
+26856
View File
File diff suppressed because it is too large Load Diff
+91
View File
@@ -0,0 +1,91 @@
{
"name": "multikart-backend",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@ag-grid-community/angular": "^27.3.0",
"@ag-grid-community/client-side-row-model": "^27.3.0",
"@ag-grid-community/core": "^27.3.0",
"@akveo/ng2-completer": "^9.0.1",
"@angular/animations": "^15.0.2",
"@angular/cdk": "^13.3.7",
"@angular/common": "^15.0.2",
"@angular/compiler": "^15.0.2",
"@angular/core": "^15.0.2",
"@angular/fire": "^7.5.0",
"@angular/forms": "^15.0.2",
"@angular/localize": "^15.0.2",
"@angular/platform-browser": "^15.0.2",
"@angular/platform-browser-dynamic": "^15.0.2",
"@angular/router": "^15.0.2",
"@ckeditor/ckeditor5-angular": "^4.0.0",
"@ckeditor/ckeditor5-build-classic": "^34.0.0",
"@ks89/angular-modal-gallery": "^9.1.0",
"@ng-bootstrap/ng-bootstrap": "^13.1.1",
"@popperjs/core": "^2.11.6",
"@swimlane/ngx-charts": "^20.1.0",
"ag-grid-angular": "^27.3.0",
"ag-grid-community": "^27.3.0",
"angular-count-to": "0.0.3",
"bootstrap": "^4.1.3",
"chart.js": "^3.7.1",
"chartist": "^0.11.4",
"express": "^4.18.1",
"feather-icons": "^4.29.0",
"firebase": "^9.17.1",
"flag-icons": "^6.3.0",
"font-awesome": "^4.7.0",
"hammerjs": "^2.0.8",
"mousetrap": "^1.6.5",
"ng-animate": "^1.0.0",
"ng-chartist": "^6.7.0",
"ng-image-slider": "^7.0.0",
"ng2-charts": "^3.0.11",
"ng2-completer": "^9.0.1",
"ng2-google-charts": "^7.0.0",
"ng2-search-filter": "^0.5.1",
"ngx-bootstrap": "^10.2.0",
"ngx-dropzone": "^3.1.0",
"ngx-dropzone-wrapper": "^11.0.0",
"ngx-owl-carousel-o": "^14.0.0",
"ngx-slimscroll": "^12.2.0",
"ngx-toastr": "^16.1.0",
"rxjs": "~7.5.5",
"rxjs-compat": "^6.6.7",
"sass": "~1.51.0",
"screenfull": "^6.0.1",
"socket.io-client": "^4.6.1",
"tslib": "^2.4.0",
"zone.js": "~0.11.5"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.2",
"@angular/cli": "^15.0.2",
"@angular/compiler-cli": "^15.0.2",
"@angular/language-service": "^15.0.2",
"@types/chart.js": "^2.9.37",
"@types/jasmine": "~4.0.3",
"@types/jasminewd2": "~2.0.10",
"@types/node": "^17.0.33",
"codelyzer": "^6.0.2",
"jasmine-core": "~4.1.1",
"jasmine-spec-reporter": "~7.0.0",
"karma": "~6.3.20",
"karma-chrome-launcher": "~3.1.1",
"karma-coverage-istanbul-reporter": "~3.0.3",
"karma-jasmine": "~5.0.1",
"karma-jasmine-html-reporter": "^1.7.0",
"protractor": "~7.0.0",
"ts-node": "~10.7.0",
"tslint": "~6.1.0",
"typescript": "~4.8.4"
}
}
+72
View File
@@ -0,0 +1,72 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { content } from './shared/routes/content-routes';
import { ContentLayoutComponent } from './shared/layout/content-layout/content-layout.component';
import { LoginComponent } from './components/auth/login/login.component';
import { SignupComponent } from './components/auth/signup/signup.component';
import { ForbiddenComponent } from './components/auth/forbidden/forbidden.component';
import { ResetPasswordComponent } from './components/auth/reset-password/reset-password.component';
import { NotLogged } from './shared/guard/not-logged.service';
import { EmailVerifiedComponent } from './components/auth/email-verified/email-verified.component';
import { EmailNotVerifiedComponent } from './components/auth/email-not-verified/email-not-verified.component';
import { SendEmailComponent } from './components/auth/send-email/send-email.component';
const routes: Routes = [
{
path: '',
redirectTo: '/dashboard/default',
pathMatch: 'full'
},
{
path: '',
component: ContentLayoutComponent,
children: content
},
{
path: 'auth/login',
canActivate: [NotLogged],
component: LoginComponent,
},
{
path: 'auth/signup',
canActivate: [NotLogged],
component: SignupComponent
},
{
path: 'auth/send-email',
canActivate: [NotLogged],
component: SendEmailComponent
},
{
path: 'auth/forbidden',
canActivate: [NotLogged],
component: ForbiddenComponent
},
{
path: 'auth/reset-password',
canActivate: [NotLogged],
component: ResetPasswordComponent
},
{
path: 'auth/email-verified',
canActivate: [NotLogged],
component: EmailVerifiedComponent
},
{
path: 'auth/email-not-verified',
canActivate: [NotLogged],
component: EmailNotVerifiedComponent
},
{
path: '**', // Navigate to Home Page if not found any page
redirectTo: '/dashboard/default',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes, {
scrollPositionRestoration: 'enabled'
})],
exports: [RouterModule]
})
export class AppRoutingModule { }
+1
View File
@@ -0,0 +1 @@
<router-outlet></router-outlet>
View File
+10
View File
@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'foodify-backend';
}
+75
View File
@@ -0,0 +1,75 @@
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { AgGridModule } from '@ag-grid-community/angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DashboardModule } from './components/dashboard/dashboard.module';
import { SharedModule } from './shared/shared.module';
import { ProductsModule } from './components/products/products.module';
import { SalesModule } from './components/sales/sales.module';
import { CouponsModule } from './components/coupons/coupons.module';
import { PagesModule } from './components/pages/pages.module';
import { MediaModule } from './components/media/media.module';
import { MenusModule } from './components/menus/menus.module';
import { VendorsModule } from './components/vendors/vendors.module';
import { UsersModule } from './components/users/users.module';
import { LocalizationModule } from './components/localization/localization.module';
import { InvoiceModule } from './components/invoice/invoice.module';
import { SettingModule } from './components/setting/setting.module';
import { ReportsModule } from './components/reports/reports.module';
import { AuthModule } from './components/auth/auth.module';
import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http";
import { ModalModule } from "ngx-bootstrap/modal";
import { AngularFireStorageModule } from "@angular/fire/compat/storage";
import { AngularFireModule } from "@angular/fire/compat";
import { environment } from "../environments/environment";
import { FirebaseService } from "./shared/service/firebase.service";
import { AuthInterceptor } from "./shared/inceptor/auth-interceptor";
import { NgImageSliderModule } from 'ng-image-slider'
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserAnimationsModule,
BrowserModule.withServerTransition({ appId: 'serverApp' }),
AppRoutingModule,
DashboardModule,
InvoiceModule,
SettingModule,
ReportsModule,
AuthModule,
SharedModule,
LocalizationModule,
ProductsModule,
SalesModule,
// NgImageSliderModule,
VendorsModule,
CouponsModule,
PagesModule,
MediaModule,
MenusModule,
UsersModule,
AgGridModule,
HttpClientModule,
AngularFireStorageModule,
AngularFireModule.initializeApp(environment.firebaseConfig, 'cloud'),
ModalModule.forRoot()
],
providers: [
FirebaseService,
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {
}
@@ -0,0 +1,45 @@
import { NgModule } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";
import { LoginComponent } from "./login/login.component";
import { ForbiddenComponent } from "./forbidden/forbidden.component";
import { SignupComponent } from "./signup/signup.component";
import { ResetPasswordComponent } from "./reset-password/reset-password.component";
import { SendEmailComponent } from "./send-email/send-email.component";
import { EmailVerifiedComponent } from "./email-verified/email-verified.component";
const routes: Routes = [
// {
// path: "login",
// component: LoginComponent,
// },
// {
// path: "forbidden",
// component: ForbiddenComponent,
// },
// {
// path: "signup",
// component: SignupComponent,
// },
// {
// path: "send-email",
// component: SendEmailComponent,
// },
// {
// path: "reset-password",
// component: ResetPasswordComponent,
// },
// {
// path: "email-verified",
// component: EmailVerifiedComponent,
// },
// {
// path: "email-not-verified",
// component: EmailVerifiedComponent,
// }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AuthRoutingModule { }
+29
View File
@@ -0,0 +1,29 @@
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { AuthRoutingModule } from "./auth-routing.module";
import { LoginComponent } from "./login/login.component";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { CarouselModule } from "ngx-owl-carousel-o";
import { SharedModule } from "../../shared/shared.module";
import { ForbiddenComponent } from "./forbidden/forbidden.component";
import { SignupComponent } from "./signup/signup.component";
import { ResetPasswordComponent } from './reset-password/reset-password.component';
import { SendEmailComponent } from './send-email/send-email.component';
import { EmailVerifiedComponent } from './email-verified/email-verified.component';
import { EmailNotVerifiedComponent } from './email-not-verified/email-not-verified.component';
@NgModule({
declarations: [LoginComponent, SignupComponent, ForbiddenComponent, ResetPasswordComponent, SendEmailComponent, EmailVerifiedComponent, EmailNotVerifiedComponent],
imports: [
CommonModule,
AuthRoutingModule,
ReactiveFormsModule,
CarouselModule,
SharedModule,
NgbModule,
FormsModule],
})
export class AuthModule { }
@@ -0,0 +1,15 @@
<!-- Container-fluid starts-->
<div class="backgound-food d-flex justify-content-center align-items-center">
<div class="forbidden-box d-flex flex-column justify-content-center align-items-center responsive-box">
<img class="forbidden-img" src="../../../../assets/images/dashboard/403-forbidden.png">
<div class="forbidden-text"><span>Email</span> Not Verified</div>
<div class="forbidden-desc mt-1 mb-4">
<div>Tài khoản của bạn chưa được xác minh.</div>
<div>Vui lòng kiểm tra email để kích hoạt tài khoản.</div>
</div>
<div class="forbidden-btn">
<button class="btn btn-primary return-btn" (click)="goToGmail()">Đi tới mail</button>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,60 @@
.backgound-food {
background-image: url("../../../../assets/images/background-login.jpg");
width: 100vw;
height: 100vh;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
z-index: -99;
.forbidden-box {
width: 822px;
// height: 536px;
background-color: #fff;
padding: 16px 110px 40px;
border-radius: 10px;
box-shadow: rgb(38, 57, 77) 0px 20px 30px -10px;
.forbidden-img {
max-width: 300px;
}
.forbidden-text {
font-family: 'Sedgwick Ave Display', cursive;
text-transform: uppercase;
font-size: 50px;
span {
color: lightcoral;
font-size: 60px;
}
}
.forbidden-desc {
font-size: 20px;
text-align: center;
font-family: 'Nunito', sans-serif;
}
.forbidden-btn {}
}
}
@media (max-width:768px) {
.backgound-food {
background-image: none;
.responsive-box {
width: 86%;
padding: 16px 20px 40px;
.forbidden-img {
max-width: 248px;
}
.forbidden-text {
text-align: center;
}
}
}
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EmailNotVerifiedComponent } from './email-not-verified.component';
describe('EmailNotVerifiedComponent', () => {
let component: EmailNotVerifiedComponent;
let fixture: ComponentFixture<EmailNotVerifiedComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ EmailNotVerifiedComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(EmailNotVerifiedComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,12 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-email-not-verified',
templateUrl: './email-not-verified.component.html',
styleUrls: ['./email-not-verified.component.scss']
})
export class EmailNotVerifiedComponent {
goToGmail() {
window.location.assign("https://mail.google.com/");
}
}
@@ -0,0 +1,16 @@
<!-- Container-fluid starts-->
<div class="backgound-food d-flex justify-content-center align-items-center">
<div class="forbidden-box d-flex flex-column justify-content-center align-items-center responsive-box">
<img class="forbidden-img" src="../../../../assets/images/dashboard/verified-vuong.png">
<div class="forbidden-text"><span>Email</span> Verified</div>
<div class="forbidden-desc mt-1 mb-4">
<div>Tài khoản của bạn đã được xác minh</div>
</div>
<div class="forbidden-btn w-100 d-flex justify-content-around align-items-center ">
<button class="btn btn-primary return-btn" [routerLink]="['/auth', 'login']">Đăng nhập cửa hàng</button>
<a href="https://foodify-fpt.vercel.app/home/login"><button class="btn btn-info return-btn">Đăng nhập
người dùng</button></a>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,60 @@
.backgound-food {
background-image: url("../../../../assets/images/background-login.jpg");
width: 100vw;
height: 100vh;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
z-index: -99;
.forbidden-box {
width: 822px;
// height: 536px;
background-color: #fff;
padding: 16px 110px 40px;
border-radius: 10px;
box-shadow: rgb(38, 57, 77) 0px 20px 30px -10px;
.forbidden-img {
max-width: 300px;
}
.forbidden-text {
font-family: 'Sedgwick Ave Display', cursive;
text-transform: uppercase;
font-size: 50px;
span {
color: rgb(128, 240, 186);
font-size: 60px;
}
}
.forbidden-desc {
font-size: 20px;
text-align: center;
font-family: 'Nunito', sans-serif;
}
.forbidden-btn {}
}
}
@media (max-width:768px) {
.backgound-food {
background-image: none;
.responsive-box {
width: 86%;
padding: 16px 20px 40px;
.forbidden-img {
max-width: 248px;
}
.forbidden-text {
text-align: center;
}
}
}
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EmailVerifiedComponent } from './email-verified.component';
describe('EmailVerifiedComponent', () => {
let component: EmailVerifiedComponent;
let fixture: ComponentFixture<EmailVerifiedComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ EmailVerifiedComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(EmailVerifiedComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-email-verified',
templateUrl: './email-verified.component.html',
styleUrls: ['./email-verified.component.scss']
})
export class EmailVerifiedComponent {
}
@@ -0,0 +1,15 @@
<!-- Container-fluid starts-->
<div class="backgound-food d-flex justify-content-center align-items-center">
<div class="forbidden-box d-flex flex-column justify-content-center align-items-center responsive-box">
<img class="forbidden-img" src="../../../../assets/images/dashboard/403-forbidden.png">
<div class="forbidden-text"><span>403</span> Forbidden</div>
<div class="forbidden-desc mt-1 mb-4">
<div>Bạn không có quyền truy cập vào trang web này</div>
<div>hoặc tài khoản của bạn đã bị khoá.</div>
</div>
<div class="forbidden-btn">
<button class="btn btn-primary return-btn" [routerLink]="['/auth', 'login']">Quay về</button>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,66 @@
.backgound-food {
background-image: url("../../../../assets/images/background-login.jpg");
width: 100vw;
height: 100vh;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
z-index: -99;
.forbidden-box {
width: 822px;
// height: 536px;
background-color: #fff;
padding: 16px 110px 40px;
border-radius: 10px;
box-shadow: rgb(38, 57, 77) 0px 20px 30px -10px;
.forbidden-img {
max-width: 300px;
}
.forbidden-text {
font-family: 'Sedgwick Ave Display', cursive;
text-transform: uppercase;
font-size: 50px;
span {
color: lightcoral;
font-size: 60px;
}
}
.forbidden-desc {
font-size: 20px;
text-align: center;
font-family: 'Nunito', sans-serif;
}
.forbidden-btn {
}
}
}
@media (max-width:768px) {
.backgound-food {
background-image: none;
.responsive-box {
width: 86%;
padding: 16px 20px 40px;
.forbidden-img {
max-width: 248px;
}
.forbidden-text {
text-align: center;
}
}
}
}
@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-forbidden',
templateUrl: './forbidden.component.html',
styleUrls: ['./forbidden.component.scss']
})
export class ForbiddenComponent {
}
@@ -0,0 +1,207 @@
<div class="page-wrapper">
<div class="authentication-box backgound-food">
<div class="container">
<div class="row">
<div class="col-md-5 p-0 card-left disable-pink">
<div class="card bg-primary">
<div class="svg-icon">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 468.52 468.52"
style="enable-background:new 0 0 468.52 468.52;" xml:space="preserve">
<g>
<path style="fill:#E6E6E6;" d="M468.52,102.595v251.88c0,11.14-7.571,20.52-17.86,23.25c-1.98,0.531-4.06,0.81-6.2,0.81H38.58
c-13.29,0-24.061-10.77-24.061-24.06v-251.88c0-13.29,10.771-24.06,24.061-24.06h405.88c2.14,0,4.22,0.28,6.2,0.81
C460.949,82.075,468.52,91.455,468.52,102.595" />
<path style="fill:#B3B3B3;" d="M468.518,102.591v23.94h-454v-23.94c0-13.29,10.771-24.06,24.06-24.06h405.88
C457.747,78.532,468.518,89.302,468.518,102.591" />
<rect x="42.099" y="141.002" style="fill:#999999;" width="118.901"
height="16.532" />
<polyline style="fill:#FFFFFF;"
points="442.383,222.765 256.652,222.765 256.652,354.496 442.383,354.496 442.383,222.765 " />
<polyline style="fill:#13c9ca;"
points="434.552,329.733 265.552,329.733 265.552,346.265 434.552,346.265 434.552,329.733 " />
<polyline style="fill:#a5a5a5;"
points="286.552,297.744 265.552,297.744 265.552,314.276 286.552,314.276 286.552,297.744 " />
<polyline style="fill:#ffbc58;"
points="350.552,265.754 265.552,265.754 265.552,282.286 350.552,282.286 350.552,265.754 " />
<polyline style="fill:#999999;"
points="411.552,233.765 265.552,233.765 265.552,250.297 411.552,250.297 411.552,233.765 " />
<circle style="fill:#a5a5a5;" cx="47.517" cy="102.532" r="15" />
<circle style="fill:#ffbc58;" cx="87.517" cy="102.532" r="15" />
<path style="fill:#EEEEEE;" d="M463.696,88.139c3.027,4.024,4.822,9.029,4.822,14.453v23.94h-23.321
c0.04,35.899-19.42,70.584-53.444,88.347c-2.102,0.921-4.223,1.779-6.365,2.574l2.774,5.313h54.222v131.731H298.563l7.326,24.038
h138.57c2.14,0,4.22-0.28,6.2-0.81c10.29-2.73,17.86-12.11,17.86-23.25v-251.88C468.52,97.17,466.725,92.164,463.696,88.139
M463.657,88.088c0.012,0.016,0.023,0.031,0.036,0.047C463.681,88.12,463.669,88.103,463.657,88.088 M463.621,88.04
c0.008,0.01,0.015,0.02,0.023,0.03C463.636,88.06,463.629,88.05,463.621,88.04 M463.59,87.999c0.003,0.004,0.006,0.008,0.009,0.012
C463.596,88.007,463.593,88.003,463.59,87.999" />
<path style="fill:#CBCBCB;" d="M444.46,78.535h-11.739c0.396,0.716,0.784,1.437,1.165,2.167c7.655,14.662,11.293,30.36,11.31,45.83
h23.321v-23.94c0-5.424-1.795-10.429-4.822-14.453l0,0c-0.001-0.001-0.002-0.002-0.003-0.003l0,0
c-0.012-0.016-0.023-0.031-0.036-0.047l0,0c-0.005-0.006-0.009-0.012-0.014-0.018l0,0c-0.008-0.01-0.015-0.02-0.023-0.03l0,0
c-0.007-0.01-0.014-0.019-0.022-0.029l0,0c-0.003-0.004-0.006-0.008-0.009-0.012l0,0c-3.187-4.171-7.708-7.269-12.93-8.654
C448.68,78.815,446.6,78.535,444.46,78.535" />
<path style="fill:#FFFFFF;" d="M442.383,222.765h-54.222l5.326,10.202c-0.519,0.271-1.04,0.537-1.561,0.798h19.626v16.532H266.807
l4.711,15.457h79.034v16.532h-73.996l4.711,15.458h5.285v16.532h-0.246l4.711,15.457h143.536v16.532H296.055l2.509,8.231h143.819
V222.765" />
<polyline style="fill:#6EC6EB;"
points="434.552,329.733 291.016,329.733 296.055,346.265 434.552,346.265 434.552,329.733 " />
<polyline style="fill:#FDA589;"
points="286.552,297.744 281.267,297.744 286.306,314.276 286.552,314.276 286.552,297.744 " />
<polyline style="fill:#FBE98E;"
points="350.552,265.754 271.518,265.754 276.556,282.286 350.552,282.286 350.552,265.754 " />
<path style="fill:#EEEEEE;" d="M246.265,126.531h-17.178l29.329,96.234h62.908c-1.465-0.282-2.926-0.591-4.384-0.929l-1.001-0.232
c-24.282-7.625-45.651-24.51-58.362-48.855C249.857,157.962,246.221,142.128,246.265,126.531" />
<path style="fill:#CBCBCB;" d="M258.73,78.535h-44.27l14.628,47.996h17.178c0.028-10.08,1.594-20.062,4.587-29.594
C252.852,90.569,255.489,84.4,258.73,78.535" />
<path style="fill:#FFFFFF;" d="M321.324,222.765h-62.908l7.136,23.415v-12.415h53.983l3.362-10.708
C322.373,222.963,321.848,222.866,321.324,222.765" />
<path style="fill:#BABABA;" d="M411.552,233.765h-19.626c-14.25,7.129-29.421,10.514-44.383,10.514
c-10.173,0-20.251-1.566-29.869-4.586l1.861-5.928h-53.983v12.415l1.255,4.117h144.745V233.765" />
<path style="fill:#CECECE;"
d="M385.387,217.453c-6.196,2.3-12.551,4.073-18.994,5.313h21.767L385.387,217.453" />
<path style="fill:#DBDBDB;"
d="M388.161,222.765h-21.767c-7.428,1.428-14.973,2.147-22.536,2.147
c-6.999,0-14.015-0.616-20.959-1.855l-3.362,10.708h72.391c0.521-0.261,1.042-0.527,1.561-0.798L388.161,222.765" />
<path style="fill:#A4A4A4;" d="M391.926,233.765h-72.391l-1.861,5.928c9.618,3.02,19.696,4.586,29.869,4.586
C362.505,244.279,377.677,240.894,391.926,233.765" />
<path style="fill:#DBDBDB;" d="M345.64,27.261c-15.507,0-31.24,3.637-45.936,11.309c-17.955,9.374-31.855,23.466-40.973,39.961
h173.987c-12.828-23.18-33.641-39.288-57.199-46.682C365.898,28.829,355.817,27.261,345.64,27.261" />
<polyline style="fill:#9F9F9F;"
points="432.719,78.532 258.732,78.532 258.73,78.535 432.721,78.535 432.719,78.532 " />
<path style="fill:#CECECE;"
d="M445.196,126.531H246.265c-0.044,15.597,3.592,31.43,11.312,46.218
c12.711,24.345,34.08,41.23,58.362,48.855l1.001,0.232c1.458,0.338,2.919,0.647,4.384,0.929h45.069
c6.443-1.239,12.797-3.012,18.994-5.313c2.142-0.795,4.263-1.653,6.365-2.574C425.776,197.115,445.237,162.431,445.196,126.531" />
<path style="fill:#B2B2B2;" d="M432.721,78.535H258.73c-3.241,5.865-5.878,12.033-7.878,18.402
c-2.993,9.532-4.559,19.515-4.587,29.594h198.931c-0.017-15.469-3.656-31.168-11.31-45.83
C433.505,79.972,433.117,79.251,432.721,78.535" />
<path style="fill:#DBDBDB;" d="M366.393,222.765h-45.069c0.524,0.101,1.049,0.198,1.574,0.292
c6.945,1.239,13.96,1.855,20.959,1.855C351.42,224.912,358.965,224.193,366.393,222.765" />
<path style="fill:#ffbc58;" d="M345.73,113.225l-5.24,16.69l-24.55,78.18c-24.28-7.62-45.65-24.51-58.36-48.85
c-12.71-24.35-14.35-51.53-6.73-75.81l83.46,26.2L345.73,113.225z" />
<path style="fill:#13c9ca;" d="M393.49,219.465c-7.48,3.9-15.23,6.76-23.08,8.63c-17.7,4.21-35.91,3.38-52.74-1.91l26.47-84.29
l3.32-10.59L393.49,219.465z" />
<path style="fill:#a5a5a5;" d="M391.75,201.375l-46.02-88.15l-11.42-3.59l-83.46-26.2c7.63-24.29,24.51-45.66,48.85-58.37
c2.55-1.33,5.12-2.54,7.73-3.62c22.27-9.33,46.35-9.93,68.09-3.1c24.29,7.62,45.66,24.5,58.37,48.85
C459.3,115.885,440.44,175.955,391.75,201.375z" />
<polyline style="fill:#FFFFFF;"
points="226.383,168.765 40.652,168.765 40.652,354.496 226.383,354.496 226.383,168.765 " />
<polyline style="fill:#2BE0C6;"
points="93.6,254.001 57.602,254.001 57.602,343.996 93.6,343.996 93.6,254.001 " />
<polyline style="fill:#ffbc58;"
points="151.517,224.002 115.518,224.002 115.518,343.996 151.517,343.996 151.517,224.002 " />
<polyline style="fill:#a5a5a5;"
points="209.432,191.004 173.435,191.004 173.435,343.996 209.432,343.996 209.432,191.004 " />
<polyline style="fill:#13c9ca;"
points="242.83,325.765 242.83,454.765 0,454.765 0,325.765 242.83,325.765 " />
<polyline style="fill:#F2F2F2;"
points="15,439.765 15,340.765 227.826,340.765 227.826,439.765 15,439.765 " />
<polyline style="fill:#a5a5a5;"
points="108.064,430.978 87.408,408.6 78.664,420.466 71.9,415.483 86.68,395.426 106.349,416.734
137.239,360.959 160.785,398.8 188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 108.064,430.978 " />
<rect x="26" y="357.765" style="fill:#999999;" width="27" height="11" />
<rect x="26" y="384.765" style="fill:#999999;" width="27" height="11" />
<rect x="26" y="411.765" style="fill:#999999;" width="27" height="11" />
<polyline style="fill:#6EC6EB;" points="242.83,325.765 209.432,325.765 173.435,325.765 151.517,325.765 115.518,325.765
93.6,325.765 92.88,325.765 97.451,340.765 227.826,340.765 227.826,439.765 127.619,439.765 132.19,454.765 242.83,454.765
242.83,325.765 " />
<polyline style="fill:#F6F6F6;" points="227.826,340.765 97.451,340.765 115.542,400.135 137.239,360.959 160.785,398.8
188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 118.951,411.32 127.619,439.765 227.826,439.765 227.826,340.765
" />
<polyline style="fill:#FDA589;" points="188.393,349.552 160.785,398.8 137.239,360.959 115.542,400.135 118.951,411.32
137.661,377.538 161.162,415.307 195.722,353.66 188.393,349.552 " />
<path style="fill:#CCCCCC;"
d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
c1.923,0,3.84-0.728,5.303-2.192c2.934-2.934,2.927-7.679,0-10.607l-25.251-25.251l20.669-8.492l-78.559-38.792L393.695,417.909z" />
<path style="fill:#808080;" d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
c1.923,0,3.84-0.728,5.303-2.192l-83.142-83.142L393.695,417.909z" />
</g>
</svg>
</div>
<div class="single-item">
<owl-carousel-o [options]="owlcarouselOptions">
<ng-container *ngFor="let data of owlcarousel">
<ng-template carouselSlide class="item">
<div>
<h3>{{data.title}}</h3>
<p>{{data.desc}}</p>
</div>
</ng-template>
</ng-container>
</owl-carousel-o>
</div>
</div>
</div>
<div class="col-md-7 p-0 card-right">
<div class="card tab2-card">
<div style="padding: 46px;" class="card-body">
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
<li [ngbNavItem]="1">
<h3 style="color: lightcoral" ngbNavLink>Đăng nhập</h3>
<ng-template ngbNavContent>
<div class="login-img-box d-flex justify-content-center align-items-center">
<img class="login-img"
src="../../../../assets/images/dashboard/Logo web.png">
</div>
<div class="tab-pane fade active show" id="account" role="tabpanel"
aria-labelledby="account-tab">
<form [formGroup]="loginForm" class="form-horizontal auth-form"
novalidate="">
<div class="form-group">
<input required="" name="login[username]" type="email"
class="form-control" placeholder="Email" formControlName="email"
id="exampleInputEmail1">
</div>
<div class="form-group">
<div class="form-control input-password-holder">
<input formControlName="password" class="input-password"
type="{{ showPassword ? 'text' : 'password' }}" #pwc
placeholder="Xác nhận mật khẩu" />
<span class="icon-holder-eye"
(click)="showPassword = !showPassword">
<i
class="{{ showPassword ? 'fa fa-eye-slash' : 'fa fa-eye' }}"></i>
</span>
</div>
<!-- <input required="" name="login[password]" type="password"
class="form-control" placeholder="Password"
formControlName="password"> -->
</div>
<div class="form-button d-flex justify-content-end">
<button class="btn btn-primary w-100 mt-0" type="submit"
(click)="onSignIn()">Đăng nhập</button>
</div>
<div class="form-terms mt-1">
<div class="d-flex justify-content-end">
<!-- <a routerLink="/reset-password" routerLinkActive="active" class="">Quên mật khẩu</a> -->
<a [routerLink]="['/auth/send-email']" routerLinkActive="active"
class="">Quên mật khẩu ?</a>
</div>
</div>
<div class="d-flex justify-content-center align-items-center mt-2">
<p class="mb-0 text-secondary">Trở thành người bán hàng của Foodify?
</p>
<a class="ml-2" [routerLink]="['/auth', 'signup']">Đăng ký ngay</a>
</div>
</form>
</div>
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="nav" class="mt-2"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<ng-template #error_modal>
<div class="modal-body text-center">
<p>Sai tên tài khoản hoặc mật khẩu. Vui lòng kiểm tra lại !</p>
<button type="button" class="btn btn-primary" (click)="closeLayer1()">Tiếp tục</button>
</div>
</ng-template>
@@ -0,0 +1,39 @@
.backgound-food {
background-image: url('../../../../assets/images/background-login.jpg');
width: 100vw;
height: 100vh;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
z-index: -99;
@media (max-width:768px) {
.disable-pink {
display: none;
}
}
.login-img-box {
width: 100%;
padding-bottom: 20px;
.login-img {
max-width: 442px;
}
}
.input-password-holder {
display: flex;
justify-content: space-between;
align-items: center;
.input-password {
border: none;
width: 100%;
}
.icon-holder-eye {
color: #999a9b;
}
}
}
@@ -0,0 +1,109 @@
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { FirebaseService } from "../../../shared/service/firebase.service";
import { Router } from '@angular/router';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
public loginForm: UntypedFormGroup;
public registerForm: UntypedFormGroup;
public active = 1;
// Password
showPassword = false;
//Modal
@ViewChild('error_modal') errorModal: TemplateRef<any>
layer1: BsModalRef;
constructor(private formBuilder: UntypedFormBuilder,
private firebaseAuthService: FirebaseService,
private modalService: BsModalService,
private router: Router) {
this.createLoginForm();
this.createRegisterForm();
}
owlcarousel = [
{
title: "Chào mừng đến với Foodify",
desc: "Foodify - Một trong những ứng dụng đặt đồ ăn tốt nhất hiện nay, được phát triển bởi Pyramide team",
},
{
title: "Foodify - xu hướng mới",
desc: "Với mục đích tạo ra những sản phẩm tốt nhất cho người dùng, chúng mình đã quyết định sử dụng các công nghệ mới nhất vào Foodify",
},
{
title: "Về Pyramide Team",
desc: "Pyramide team là một nhóm sinh viên trường Đại học FPT, team nghiên cứu và phát triển sản phẩm theo công nghệ Angular và Spring Boot",
}
]
owlcarouselOptions = {
loop: true,
items: 1,
dots: true
};
createLoginForm() {
this.loginForm = this.formBuilder.group({
email: [''],
password: [''],
});
}
createRegisterForm() {
this.registerForm = this.formBuilder.group({
email: [''],
password: [''],
confirmPassword: [''],
});
}
ngOnInit() {
}
onSubmit() {
}
onSignUp() {
this.firebaseAuthService.signUp(this.getSignUpEmail, this.getSignUpPassword);
}
onSignIn() {
this.firebaseAuthService.signIn(this.getSignInEmail, this.getSignInPassword).then((result) => {
if (result) {
}
else {
this.layer1 = this.modalService.show(this.errorModal, { class: "modal-sm" });
}
});
}
closeLayer1() {
this.layer1.hide();
}
get getSignUpEmail() {
return this.registerForm.get('email').getRawValue();
}
get getSignUpPassword() {
return this.registerForm.get('password').getRawValue();
}
get getSignInEmail() {
return this.loginForm.get('email').getRawValue();
}
get getSignInPassword() {
return this.loginForm.get('password').getRawValue();
}
}
@@ -0,0 +1,216 @@
<div class="page-wrapper">
<div class="authentication-box backgound-food">
<div class="container">
<div class="row">
<div class="col-md-5 p-0 card-left disable-pink">
<div class="card bg-primary">
<div class="svg-icon">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 468.52 468.52"
style="enable-background: new 0 0 468.52 468.52" xml:space="preserve">
<g>
<path style="fill: #e6e6e6" d="M468.52,102.595v251.88c0,11.14-7.571,20.52-17.86,23.25c-1.98,0.531-4.06,0.81-6.2,0.81H38.58
c-13.29,0-24.061-10.77-24.061-24.06v-251.88c0-13.29,10.771-24.06,24.061-24.06h405.88c2.14,0,4.22,0.28,6.2,0.81
C460.949,82.075,468.52,91.455,468.52,102.595" />
<path style="fill: #b3b3b3" d="M468.518,102.591v23.94h-454v-23.94c0-13.29,10.771-24.06,24.06-24.06h405.88
C457.747,78.532,468.518,89.302,468.518,102.591" />
<rect x="42.099" y="141.002" style="fill: #999999" width="118.901" height="16.532" />
<polyline style="fill: #ffffff"
points="442.383,222.765 256.652,222.765 256.652,354.496 442.383,354.496 442.383,222.765 " />
<polyline style="fill: #13c9ca"
points="434.552,329.733 265.552,329.733 265.552,346.265 434.552,346.265 434.552,329.733 " />
<polyline style="fill: #a5a5a5"
points="286.552,297.744 265.552,297.744 265.552,314.276 286.552,314.276 286.552,297.744 " />
<polyline style="fill: #ffbc58"
points="350.552,265.754 265.552,265.754 265.552,282.286 350.552,282.286 350.552,265.754 " />
<polyline style="fill: #999999"
points="411.552,233.765 265.552,233.765 265.552,250.297 411.552,250.297 411.552,233.765 " />
<circle style="fill: #a5a5a5" cx="47.517" cy="102.532" r="15" />
<circle style="fill: #ffbc58" cx="87.517" cy="102.532" r="15" />
<path style="fill: #eeeeee" d="M463.696,88.139c3.027,4.024,4.822,9.029,4.822,14.453v23.94h-23.321
c0.04,35.899-19.42,70.584-53.444,88.347c-2.102,0.921-4.223,1.779-6.365,2.574l2.774,5.313h54.222v131.731H298.563l7.326,24.038
h138.57c2.14,0,4.22-0.28,6.2-0.81c10.29-2.73,17.86-12.11,17.86-23.25v-251.88C468.52,97.17,466.725,92.164,463.696,88.139
M463.657,88.088c0.012,0.016,0.023,0.031,0.036,0.047C463.681,88.12,463.669,88.103,463.657,88.088 M463.621,88.04
c0.008,0.01,0.015,0.02,0.023,0.03C463.636,88.06,463.629,88.05,463.621,88.04 M463.59,87.999c0.003,0.004,0.006,0.008,0.009,0.012
C463.596,88.007,463.593,88.003,463.59,87.999" />
<path style="fill: #cbcbcb" d="M444.46,78.535h-11.739c0.396,0.716,0.784,1.437,1.165,2.167c7.655,14.662,11.293,30.36,11.31,45.83
h23.321v-23.94c0-5.424-1.795-10.429-4.822-14.453l0,0c-0.001-0.001-0.002-0.002-0.003-0.003l0,0
c-0.012-0.016-0.023-0.031-0.036-0.047l0,0c-0.005-0.006-0.009-0.012-0.014-0.018l0,0c-0.008-0.01-0.015-0.02-0.023-0.03l0,0
c-0.007-0.01-0.014-0.019-0.022-0.029l0,0c-0.003-0.004-0.006-0.008-0.009-0.012l0,0c-3.187-4.171-7.708-7.269-12.93-8.654
C448.68,78.815,446.6,78.535,444.46,78.535" />
<path style="fill: #ffffff" d="M442.383,222.765h-54.222l5.326,10.202c-0.519,0.271-1.04,0.537-1.561,0.798h19.626v16.532H266.807
l4.711,15.457h79.034v16.532h-73.996l4.711,15.458h5.285v16.532h-0.246l4.711,15.457h143.536v16.532H296.055l2.509,8.231h143.819
V222.765" />
<polyline style="fill: #6ec6eb"
points="434.552,329.733 291.016,329.733 296.055,346.265 434.552,346.265 434.552,329.733 " />
<polyline style="fill: #fda589"
points="286.552,297.744 281.267,297.744 286.306,314.276 286.552,314.276 286.552,297.744 " />
<polyline style="fill: #fbe98e"
points="350.552,265.754 271.518,265.754 276.556,282.286 350.552,282.286 350.552,265.754 " />
<path style="fill: #eeeeee" d="M246.265,126.531h-17.178l29.329,96.234h62.908c-1.465-0.282-2.926-0.591-4.384-0.929l-1.001-0.232
c-24.282-7.625-45.651-24.51-58.362-48.855C249.857,157.962,246.221,142.128,246.265,126.531" />
<path style="fill: #cbcbcb" d="M258.73,78.535h-44.27l14.628,47.996h17.178c0.028-10.08,1.594-20.062,4.587-29.594
C252.852,90.569,255.489,84.4,258.73,78.535" />
<path style="fill: #ffffff" d="M321.324,222.765h-62.908l7.136,23.415v-12.415h53.983l3.362-10.708
C322.373,222.963,321.848,222.866,321.324,222.765" />
<path style="fill: #bababa" d="M411.552,233.765h-19.626c-14.25,7.129-29.421,10.514-44.383,10.514
c-10.173,0-20.251-1.566-29.869-4.586l1.861-5.928h-53.983v12.415l1.255,4.117h144.745V233.765" />
<path style="fill: #cecece"
d="M385.387,217.453c-6.196,2.3-12.551,4.073-18.994,5.313h21.767L385.387,217.453" />
<path style="fill: #dbdbdb"
d="M388.161,222.765h-21.767c-7.428,1.428-14.973,2.147-22.536,2.147
c-6.999,0-14.015-0.616-20.959-1.855l-3.362,10.708h72.391c0.521-0.261,1.042-0.527,1.561-0.798L388.161,222.765" />
<path style="fill: #a4a4a4" d="M391.926,233.765h-72.391l-1.861,5.928c9.618,3.02,19.696,4.586,29.869,4.586
C362.505,244.279,377.677,240.894,391.926,233.765" />
<path style="fill: #dbdbdb" d="M345.64,27.261c-15.507,0-31.24,3.637-45.936,11.309c-17.955,9.374-31.855,23.466-40.973,39.961
h173.987c-12.828-23.18-33.641-39.288-57.199-46.682C365.898,28.829,355.817,27.261,345.64,27.261" />
<polyline style="fill: #9f9f9f"
points="432.719,78.532 258.732,78.532 258.73,78.535 432.721,78.535 432.719,78.532 " />
<path style="fill: #cecece"
d="M445.196,126.531H246.265c-0.044,15.597,3.592,31.43,11.312,46.218
c12.711,24.345,34.08,41.23,58.362,48.855l1.001,0.232c1.458,0.338,2.919,0.647,4.384,0.929h45.069
c6.443-1.239,12.797-3.012,18.994-5.313c2.142-0.795,4.263-1.653,6.365-2.574C425.776,197.115,445.237,162.431,445.196,126.531" />
<path style="fill: #b2b2b2" d="M432.721,78.535H258.73c-3.241,5.865-5.878,12.033-7.878,18.402
c-2.993,9.532-4.559,19.515-4.587,29.594h198.931c-0.017-15.469-3.656-31.168-11.31-45.83
C433.505,79.972,433.117,79.251,432.721,78.535" />
<path style="fill: #dbdbdb" d="M366.393,222.765h-45.069c0.524,0.101,1.049,0.198,1.574,0.292
c6.945,1.239,13.96,1.855,20.959,1.855C351.42,224.912,358.965,224.193,366.393,222.765" />
<path style="fill: #ffbc58" d="M345.73,113.225l-5.24,16.69l-24.55,78.18c-24.28-7.62-45.65-24.51-58.36-48.85
c-12.71-24.35-14.35-51.53-6.73-75.81l83.46,26.2L345.73,113.225z" />
<path style="fill: #13c9ca" d="M393.49,219.465c-7.48,3.9-15.23,6.76-23.08,8.63c-17.7,4.21-35.91,3.38-52.74-1.91l26.47-84.29
l3.32-10.59L393.49,219.465z" />
<path style="fill: #a5a5a5" d="M391.75,201.375l-46.02-88.15l-11.42-3.59l-83.46-26.2c7.63-24.29,24.51-45.66,48.85-58.37
c2.55-1.33,5.12-2.54,7.73-3.62c22.27-9.33,46.35-9.93,68.09-3.1c24.29,7.62,45.66,24.5,58.37,48.85
C459.3,115.885,440.44,175.955,391.75,201.375z" />
<polyline style="fill: #ffffff"
points="226.383,168.765 40.652,168.765 40.652,354.496 226.383,354.496 226.383,168.765 " />
<polyline style="fill: #2be0c6"
points="93.6,254.001 57.602,254.001 57.602,343.996 93.6,343.996 93.6,254.001 " />
<polyline style="fill: #ffbc58"
points="151.517,224.002 115.518,224.002 115.518,343.996 151.517,343.996 151.517,224.002 " />
<polyline style="fill: #a5a5a5"
points="209.432,191.004 173.435,191.004 173.435,343.996 209.432,343.996 209.432,191.004 " />
<polyline style="fill: #13c9ca"
points="242.83,325.765 242.83,454.765 0,454.765 0,325.765 242.83,325.765 " />
<polyline style="fill: #f2f2f2"
points="15,439.765 15,340.765 227.826,340.765 227.826,439.765 15,439.765 " />
<polyline style="fill: #a5a5a5"
points="108.064,430.978 87.408,408.6 78.664,420.466 71.9,415.483 86.68,395.426 106.349,416.734
137.239,360.959 160.785,398.8 188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 108.064,430.978 " />
<rect x="26" y="357.765" style="fill: #999999" width="27" height="11" />
<rect x="26" y="384.765" style="fill: #999999" width="27" height="11" />
<rect x="26" y="411.765" style="fill: #999999" width="27" height="11" />
<polyline style="fill: #6ec6eb" points="242.83,325.765 209.432,325.765 173.435,325.765 151.517,325.765 115.518,325.765
93.6,325.765 92.88,325.765 97.451,340.765 227.826,340.765 227.826,439.765 127.619,439.765 132.19,454.765 242.83,454.765
242.83,325.765 " />
<polyline style="fill: #f6f6f6" points="227.826,340.765 97.451,340.765 115.542,400.135 137.239,360.959 160.785,398.8
188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 118.951,411.32 127.619,439.765 227.826,439.765 227.826,340.765
" />
<polyline style="fill: #fda589" points="188.393,349.552 160.785,398.8 137.239,360.959 115.542,400.135 118.951,411.32
137.661,377.538 161.162,415.307 195.722,353.66 188.393,349.552 " />
<path style="fill: #cccccc"
d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
c1.923,0,3.84-0.728,5.303-2.192c2.934-2.934,2.927-7.679,0-10.607l-25.251-25.251l20.669-8.492l-78.559-38.792L393.695,417.909z" />
<path style="fill: #808080" d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
c1.923,0,3.84-0.728,5.303-2.192l-83.142-83.142L393.695,417.909z" />
</g>
</svg>
</div>
<div class="single-item">
<owl-carousel-o [options]="owlcarouselOptions">
<ng-container *ngFor="let data of owlcarousel">
<ng-template carouselSlide class="item">
<div>
<h3>{{ data.title }}</h3>
<p>{{ data.desc }}</p>
</div>
</ng-template>
</ng-container>
</owl-carousel-o>
</div>
</div>
</div>
<div class="col-md-7 p-0 card-right">
<div class="card tab2-card">
<div class="card-body">
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
<li [ngbNavItem]="1">
<h3 style="color: lightcoral" ngbNavLink>Thay đổi mật khẩu mới</h3>
<ng-template ngbNavContent>
<div class="reset-img-box d-flex justify-content-center align-items-center">
<img class="reset-img" src="../../../../assets/images/dashboard/reset-password.png">
</div>
<div class="tab-pane fade active show" id="account" role="tabpanel" aria-labelledby="account-tab">
<form [formGroup]="resetPasswordForm" class="form-horizontal auth-form" novalidate="">
<div class="form-group">
<div class="form-control input-password-holder">
<input formControlName="password" class="input-password"
type="{{ showPassword ? 'text' : 'password' }}" placeholder="Mật khẩu mới" />
<span class="icon-holder-eye" (click)="showPassword = !showPassword">
<i class="{{ showPassword ? 'fa fa-eye-slash' : 'fa fa-eye' }}"></i>
</span>
</div>
<div *ngIf="password?.invalid && (password?.dirty || password?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="password?.errors?.['required']">Vui lòng không được bỏ trống mật khẩu.</div>
<div *ngIf="password?.errors?.['pattern']">
Mật khẩu phải chứa ít nhất 8 ký tự, trong đó gồm một ký tự số, một chữ hoa, một chữ thường
và một ký tự đặc biệt.
<br />
Mật khẩu không được chứa khoảng trắng.
</div>
</div>
</div>
<div class="form-group">
<div class="form-control input-password-holder">
<input formControlName="confirmPassword" class="input-password"
type="{{ showPassword ? 'text' : 'password' }}" #pwc placeholder="Xác nhận mật khẩu" />
<span class="icon-holder-eye" (click)="showPassword = !showPassword">
<i class="{{ showPassword ? 'fa fa-eye-slash' : 'fa fa-eye' }}"></i>
</span>
</div>
<div *ngIf="
confirmPassword?.invalid &&
(confirmPassword?.dirty || confirmPassword?.touched)
" class="alert alert-danger mt-1">
<div *ngIf="confirmPassword?.errors?.['required']">Vui lòng xác nhận mật khẩu.</div>
<div *ngIf="confirmPassword?.errors?.['pattern']">
Mật khẩu phải chứa ít nhất 8 ký tự, trong đó gồm một ký tự số, một chữ hoa, một chữ thường
và một ký tự đặc biệt.
<br />
Mật khẩu không được chứa khoảng trắng.
</div>
<div *ngIf="confirmPassword?.errors?.['confirmedValidator']">
Mật khẩu xác nhận phải giống với mật khẩu đã nhập
</div>
</div>
</div>
<div class="form-button">
<button class="btn btn-primary" type="submit"
(click)="changePassword(changePasswordCompleted)">Thay đổi</button>
</div>
</form>
</div>
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="nav" class="mt-2"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<ng-template #changePasswordCompleted>
<div class="modal-body text-center">
<p>Thay đổi mật khẩu thành công</p>
<button type="button" class="btn btn-primary" (click)="backToLogin()"> Đăng nhập ngay
</button>
</div>
</ng-template>
@@ -0,0 +1,39 @@
.backgound-food {
background-image: url('../../../../assets/images/background-login.jpg');
width: 100vw;
height: 100vh;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
z-index: -99;
@media (max-width:768px) {
.disable-pink {
display: none;
}
}
.reset-img-box {
.reset-img {
max-width: 265px;
padding-bottom: 15px;
}
}
}
.input-password-holder {
display: flex;
justify-content: space-between;
align-items: center;
.input-password {
border: none;
width: 100%;
}
.icon-holder-eye {
color: #999a9b;
}
}
@@ -0,0 +1,140 @@
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { FormControl, FormGroup, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { FirebaseService } from "../../../shared/service/firebase.service";
import { ActivatedRoute, Router } from '@angular/router';
import { Validation } from 'src/app/constants/Validation';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
@Component({
selector: "app-reset-password",
templateUrl: "./reset-password.component.html",
styleUrls: ["./reset-password.component.scss"],
})
export class ResetPasswordComponent {
public resetPasswordForm: FormGroup
public active = 1;
// Password
action: string = '';
oobCode: string = '';
showPassword = false;
//Modal
layer1: BsModalRef;
constructor(
private formBuilder: UntypedFormBuilder,
private activatedRoute: ActivatedRoute,
private firebaseService: FirebaseService,
private modalService: BsModalService,
private router: Router
) {
}
owlcarousel = [
{
title: "Chào mừng đến với Foodify",
desc: "Foodify - Một trong những ứng dụng đặt đồ ăn tốt nhất hiện nay, được phát triển bởi Pyramide team",
},
{
title: "Foodify - xu hướng mới",
desc: "Với mục đích tạo ra những sản phẩm tốt nhất cho người dùng, chúng mình đã quyết định sử dụng các công nghệ mới nhất vào Foodify",
},
{
title: "Về Pyramide Team",
desc: "Pyramide team là một nhóm sinh viên trường Đại học FPT, team nghiên cứu và phát triển sản phẩm theo công nghệ Angular và Spring Boot",
},
];
owlcarouselOptions = {
loop: true,
items: 1,
dots: true,
};
ngOnInit() {
//Get Token from link
this.oobCode = this.activatedRoute.snapshot.queryParamMap.get('oobCode');
this.action = this.activatedRoute.snapshot.queryParamMap.get('mode');
if (this.action == 'verifyEmail') {
this.firebaseService.confirmVerifyEmail(this.oobCode)
this.router.navigate(['/auth/email-verified'])
}
if (this.oobCode) {
this.resetPasswordForm = this.formBuilder.group({
password: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.Password)]),
confirmPassword: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.Password)]),
},
{
validator: this.ConfirmedValidator("password", "confirmPassword"),
})
}
else {
this.router.navigate(['/auth/forbidden']);
}
}
//Change password
changePassword(success: TemplateRef<any>) {
if (this.resetPasswordForm.invalid) {
this.resetPasswordForm.markAllAsTouched();
return;
}
this.firebaseService.confirmPasswordReset(this.oobCode, this.password.value);
this.layer1 = this.modalService.show(success, { class: 'modal-sm' })
}
backToLogin() {
this.layer1.hide();
this.router.navigate(['/auth/login']);
}
// Validation for password and confirm password
ConfirmedValidator(controlName: string, matchingControlName: string) {
return (formGroup: FormGroup) => {
const control = formGroup.controls[controlName];
const matchingControl = formGroup.controls[matchingControlName];
if (matchingControl.errors && !matchingControl.errors.confirmedValidator) {
return;
}
if (control.value !== matchingControl.value) {
matchingControl.setErrors({ confirmedValidator: true });
} else {
matchingControl.setErrors(null);
}
};
}
get password() {
return this.resetPasswordForm.get("password");
}
get confirmPassword() {
return this.resetPasswordForm.get("confirmPassword");
}
// onSignUp() {
// this.firebaseAuthService.signUp(this.getSignUpEmail, this.getSignUpPassword);
// }
// onSignIn() {
// this.firebaseAuthService.signIn(this.getSignInEmail, this.getSignInPassword);
// }
// get getSignUpPassword() {
// return this.registerForm.get("password").getRawValue();
// }
// get getSignInEmail() {
// return this.loginForm.get("email").getRawValue();
// }
// get getSignInPassword() {
// return this.loginForm.get("password").getRawValue();
// }
}
@@ -0,0 +1,183 @@
<div class="page-wrapper">
<div class="authentication-box backgound-food">
<div class="container">
<div class="row">
<div class="col-md-5 p-0 card-left disable-pink">
<div class="card bg-primary">
<div class="svg-icon">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 468.52 468.52"
style="enable-background: new 0 0 468.52 468.52" xml:space="preserve">
<g>
<path style="fill: #e6e6e6" d="M468.52,102.595v251.88c0,11.14-7.571,20.52-17.86,23.25c-1.98,0.531-4.06,0.81-6.2,0.81H38.58
c-13.29,0-24.061-10.77-24.061-24.06v-251.88c0-13.29,10.771-24.06,24.061-24.06h405.88c2.14,0,4.22,0.28,6.2,0.81
C460.949,82.075,468.52,91.455,468.52,102.595" />
<path style="fill: #b3b3b3" d="M468.518,102.591v23.94h-454v-23.94c0-13.29,10.771-24.06,24.06-24.06h405.88
C457.747,78.532,468.518,89.302,468.518,102.591" />
<rect x="42.099" y="141.002" style="fill: #999999" width="118.901" height="16.532" />
<polyline style="fill: #ffffff"
points="442.383,222.765 256.652,222.765 256.652,354.496 442.383,354.496 442.383,222.765 " />
<polyline style="fill: #13c9ca"
points="434.552,329.733 265.552,329.733 265.552,346.265 434.552,346.265 434.552,329.733 " />
<polyline style="fill: #a5a5a5"
points="286.552,297.744 265.552,297.744 265.552,314.276 286.552,314.276 286.552,297.744 " />
<polyline style="fill: #ffbc58"
points="350.552,265.754 265.552,265.754 265.552,282.286 350.552,282.286 350.552,265.754 " />
<polyline style="fill: #999999"
points="411.552,233.765 265.552,233.765 265.552,250.297 411.552,250.297 411.552,233.765 " />
<circle style="fill: #a5a5a5" cx="47.517" cy="102.532" r="15" />
<circle style="fill: #ffbc58" cx="87.517" cy="102.532" r="15" />
<path style="fill: #eeeeee" d="M463.696,88.139c3.027,4.024,4.822,9.029,4.822,14.453v23.94h-23.321
c0.04,35.899-19.42,70.584-53.444,88.347c-2.102,0.921-4.223,1.779-6.365,2.574l2.774,5.313h54.222v131.731H298.563l7.326,24.038
h138.57c2.14,0,4.22-0.28,6.2-0.81c10.29-2.73,17.86-12.11,17.86-23.25v-251.88C468.52,97.17,466.725,92.164,463.696,88.139
M463.657,88.088c0.012,0.016,0.023,0.031,0.036,0.047C463.681,88.12,463.669,88.103,463.657,88.088 M463.621,88.04
c0.008,0.01,0.015,0.02,0.023,0.03C463.636,88.06,463.629,88.05,463.621,88.04 M463.59,87.999c0.003,0.004,0.006,0.008,0.009,0.012
C463.596,88.007,463.593,88.003,463.59,87.999" />
<path style="fill: #cbcbcb" d="M444.46,78.535h-11.739c0.396,0.716,0.784,1.437,1.165,2.167c7.655,14.662,11.293,30.36,11.31,45.83
h23.321v-23.94c0-5.424-1.795-10.429-4.822-14.453l0,0c-0.001-0.001-0.002-0.002-0.003-0.003l0,0
c-0.012-0.016-0.023-0.031-0.036-0.047l0,0c-0.005-0.006-0.009-0.012-0.014-0.018l0,0c-0.008-0.01-0.015-0.02-0.023-0.03l0,0
c-0.007-0.01-0.014-0.019-0.022-0.029l0,0c-0.003-0.004-0.006-0.008-0.009-0.012l0,0c-3.187-4.171-7.708-7.269-12.93-8.654
C448.68,78.815,446.6,78.535,444.46,78.535" />
<path style="fill: #ffffff" d="M442.383,222.765h-54.222l5.326,10.202c-0.519,0.271-1.04,0.537-1.561,0.798h19.626v16.532H266.807
l4.711,15.457h79.034v16.532h-73.996l4.711,15.458h5.285v16.532h-0.246l4.711,15.457h143.536v16.532H296.055l2.509,8.231h143.819
V222.765" />
<polyline style="fill: #6ec6eb"
points="434.552,329.733 291.016,329.733 296.055,346.265 434.552,346.265 434.552,329.733 " />
<polyline style="fill: #fda589"
points="286.552,297.744 281.267,297.744 286.306,314.276 286.552,314.276 286.552,297.744 " />
<polyline style="fill: #fbe98e"
points="350.552,265.754 271.518,265.754 276.556,282.286 350.552,282.286 350.552,265.754 " />
<path style="fill: #eeeeee" d="M246.265,126.531h-17.178l29.329,96.234h62.908c-1.465-0.282-2.926-0.591-4.384-0.929l-1.001-0.232
c-24.282-7.625-45.651-24.51-58.362-48.855C249.857,157.962,246.221,142.128,246.265,126.531" />
<path style="fill: #cbcbcb" d="M258.73,78.535h-44.27l14.628,47.996h17.178c0.028-10.08,1.594-20.062,4.587-29.594
C252.852,90.569,255.489,84.4,258.73,78.535" />
<path style="fill: #ffffff" d="M321.324,222.765h-62.908l7.136,23.415v-12.415h53.983l3.362-10.708
C322.373,222.963,321.848,222.866,321.324,222.765" />
<path style="fill: #bababa" d="M411.552,233.765h-19.626c-14.25,7.129-29.421,10.514-44.383,10.514
c-10.173,0-20.251-1.566-29.869-4.586l1.861-5.928h-53.983v12.415l1.255,4.117h144.745V233.765" />
<path style="fill: #cecece"
d="M385.387,217.453c-6.196,2.3-12.551,4.073-18.994,5.313h21.767L385.387,217.453" />
<path style="fill: #dbdbdb"
d="M388.161,222.765h-21.767c-7.428,1.428-14.973,2.147-22.536,2.147
c-6.999,0-14.015-0.616-20.959-1.855l-3.362,10.708h72.391c0.521-0.261,1.042-0.527,1.561-0.798L388.161,222.765" />
<path style="fill: #a4a4a4" d="M391.926,233.765h-72.391l-1.861,5.928c9.618,3.02,19.696,4.586,29.869,4.586
C362.505,244.279,377.677,240.894,391.926,233.765" />
<path style="fill: #dbdbdb" d="M345.64,27.261c-15.507,0-31.24,3.637-45.936,11.309c-17.955,9.374-31.855,23.466-40.973,39.961
h173.987c-12.828-23.18-33.641-39.288-57.199-46.682C365.898,28.829,355.817,27.261,345.64,27.261" />
<polyline style="fill: #9f9f9f"
points="432.719,78.532 258.732,78.532 258.73,78.535 432.721,78.535 432.719,78.532 " />
<path style="fill: #cecece"
d="M445.196,126.531H246.265c-0.044,15.597,3.592,31.43,11.312,46.218
c12.711,24.345,34.08,41.23,58.362,48.855l1.001,0.232c1.458,0.338,2.919,0.647,4.384,0.929h45.069
c6.443-1.239,12.797-3.012,18.994-5.313c2.142-0.795,4.263-1.653,6.365-2.574C425.776,197.115,445.237,162.431,445.196,126.531" />
<path style="fill: #b2b2b2" d="M432.721,78.535H258.73c-3.241,5.865-5.878,12.033-7.878,18.402
c-2.993,9.532-4.559,19.515-4.587,29.594h198.931c-0.017-15.469-3.656-31.168-11.31-45.83
C433.505,79.972,433.117,79.251,432.721,78.535" />
<path style="fill: #dbdbdb" d="M366.393,222.765h-45.069c0.524,0.101,1.049,0.198,1.574,0.292
c6.945,1.239,13.96,1.855,20.959,1.855C351.42,224.912,358.965,224.193,366.393,222.765" />
<path style="fill: #ffbc58" d="M345.73,113.225l-5.24,16.69l-24.55,78.18c-24.28-7.62-45.65-24.51-58.36-48.85
c-12.71-24.35-14.35-51.53-6.73-75.81l83.46,26.2L345.73,113.225z" />
<path style="fill: #13c9ca" d="M393.49,219.465c-7.48,3.9-15.23,6.76-23.08,8.63c-17.7,4.21-35.91,3.38-52.74-1.91l26.47-84.29
l3.32-10.59L393.49,219.465z" />
<path style="fill: #a5a5a5" d="M391.75,201.375l-46.02-88.15l-11.42-3.59l-83.46-26.2c7.63-24.29,24.51-45.66,48.85-58.37
c2.55-1.33,5.12-2.54,7.73-3.62c22.27-9.33,46.35-9.93,68.09-3.1c24.29,7.62,45.66,24.5,58.37,48.85
C459.3,115.885,440.44,175.955,391.75,201.375z" />
<polyline style="fill: #ffffff"
points="226.383,168.765 40.652,168.765 40.652,354.496 226.383,354.496 226.383,168.765 " />
<polyline style="fill: #2be0c6"
points="93.6,254.001 57.602,254.001 57.602,343.996 93.6,343.996 93.6,254.001 " />
<polyline style="fill: #ffbc58"
points="151.517,224.002 115.518,224.002 115.518,343.996 151.517,343.996 151.517,224.002 " />
<polyline style="fill: #a5a5a5"
points="209.432,191.004 173.435,191.004 173.435,343.996 209.432,343.996 209.432,191.004 " />
<polyline style="fill: #13c9ca"
points="242.83,325.765 242.83,454.765 0,454.765 0,325.765 242.83,325.765 " />
<polyline style="fill: #f2f2f2"
points="15,439.765 15,340.765 227.826,340.765 227.826,439.765 15,439.765 " />
<polyline style="fill: #a5a5a5"
points="108.064,430.978 87.408,408.6 78.664,420.466 71.9,415.483 86.68,395.426 106.349,416.734
137.239,360.959 160.785,398.8 188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 108.064,430.978 " />
<rect x="26" y="357.765" style="fill: #999999" width="27" height="11" />
<rect x="26" y="384.765" style="fill: #999999" width="27" height="11" />
<rect x="26" y="411.765" style="fill: #999999" width="27" height="11" />
<polyline style="fill: #6ec6eb" points="242.83,325.765 209.432,325.765 173.435,325.765 151.517,325.765 115.518,325.765
93.6,325.765 92.88,325.765 97.451,340.765 227.826,340.765 227.826,439.765 127.619,439.765 132.19,454.765 242.83,454.765
242.83,325.765 " />
<polyline style="fill: #f6f6f6" points="227.826,340.765 97.451,340.765 115.542,400.135 137.239,360.959 160.785,398.8
188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 118.951,411.32 127.619,439.765 227.826,439.765 227.826,340.765
" />
<polyline style="fill: #fda589" points="188.393,349.552 160.785,398.8 137.239,360.959 115.542,400.135 118.951,411.32
137.661,377.538 161.162,415.307 195.722,353.66 188.393,349.552 " />
<path style="fill: #cccccc"
d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
c1.923,0,3.84-0.728,5.303-2.192c2.934-2.934,2.927-7.679,0-10.607l-25.251-25.251l20.669-8.492l-78.559-38.792L393.695,417.909z" />
<path style="fill: #808080" d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
c1.923,0,3.84-0.728,5.303-2.192l-83.142-83.142L393.695,417.909z" />
</g>
</svg>
</div>
<div class="single-item">
<owl-carousel-o [options]="owlcarouselOptions">
<ng-container *ngFor="let data of owlcarousel">
<ng-template carouselSlide class="item">
<div>
<h3>{{ data.title }}</h3>
<p>{{ data.desc }}</p>
</div>
</ng-template>
</ng-container>
</owl-carousel-o>
</div>
</div>
</div>
<div class="col-md-7 p-0 card-right">
<div class="card tab2-card">
<div class="card-body">
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
<li [ngbNavItem]="1">
<h3 style="color: lightcoral" ngbNavLink>Quên mật khẩu</h3>
<ng-template ngbNavContent>
<div class="email-img-box d-flex justify-content-center align-items-center">
<img class="email-img" src="../../../../assets/images/dashboard/email.png">
</div>
<div class="tab-pane fade active show" id="account" role="tabpanel" aria-labelledby="account-tab">
<form [formGroup]="sendEmailForm" class="form-horizontal auth-form" novalidate="">
<div class="form-group d-flex justify-content-center align-items-center ">
<i class="fa fa-envelope-o" aria-hidden="true"></i>
<input type="email" class="form-control" placeholder="Vui lòng nhập Email"
formControlName="email" />
</div>
<div *ngIf="email?.invalid && (email?.dirty || email?.touched)" class="alert alert-danger">
<div *ngIf="email?.errors?.['required']">Vui lòng không được bỏ trống email</div>
<div *ngIf="email?.errors?.email">Email không hợp lệ, vui lòng nhập lại</div>
</div>
<div class="form-button d-flex justify-content-around">
<button class="btn btn-primary" type="submit" (click)="sendPasswordResetMail()">Tiếp
tục</button>
<button type="button" class="btn btn-danger btn-back" [routerLink]="['/auth', 'login']">Quay
lại</button>
</div>
</form>
</div>
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="nav" class="mt-2"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<ng-template #sendMailCompleted>
<div class="modal-body text-center">
<p>Đã gửi đường dẫn thay đổi mật khẩu đến email của bạn</p>
<p>Vui lòng kiểm tra hòm thư</p>
<button type="button" class="btn btn-primary" (click)="gmail()">Gmail</button>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<button type="button" class="btn btn-secondary" (click)="closeLayer1()">Đóng</button>
</div>
</ng-template>
@@ -0,0 +1,38 @@
.backgound-food {
background-image: url("../../../../assets/images/background-login.jpg");
width: 100vw;
height: 100vh;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
z-index: -99;
@media (max-width:768px) {
.disable-pink {
display: none;
}
}
.email-img-box {
.email-img {
max-width: 351px;
padding-bottom: 20px;
}
}
.fa-envelope-o {
font-size: 38px;
margin-right: 12px;
color: #8b8b8b;
}
}
.btn-back {
border-radius: 25px;
margin-top: 20px;
font-weight: 400;
padding: 11px 45px;
}
@@ -0,0 +1,81 @@
import { Component, OnInit, TemplateRef, ViewChild } from "@angular/core";
import { FormControl, FormGroup, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
import { FirebaseService } from "../../../shared/service/firebase.service";
import { Router } from "@angular/router";
import { Validation } from "src/app/constants/Validation";
import { BsModalRef, BsModalService } from "ngx-bootstrap/modal";
@Component({
selector: "app-send-email",
templateUrl: "./send-email.component.html",
styleUrls: ["./send-email.component.scss"],
})
// @ViewChild('sendMailCompleted') sendMailCompleted: TemplateRef<any>;
export class SendEmailComponent {
public sendEmailForm: FormGroup;
public active = 1;
@ViewChild('sendMailCompleted') sendMailCompleted: TemplateRef<any>
constructor(
private formBuilder: UntypedFormBuilder,
private firebaseService: FirebaseService,
private modalService: BsModalService,
private router: Router
) {
}
layer1: BsModalRef;
owlcarousel = [
{
title: "Chào mừng đến với Foodify",
desc: "Foodify - Một trong những ứng dụng đặt đồ ăn tốt nhất hiện nay, được phát triển bởi Pyramide team",
},
{
title: "Foodify - xu hướng mới",
desc: "Với mục đích tạo ra những sản phẩm tốt nhất cho người dùng, chúng mình đã quyết định sử dụng các công nghệ mới nhất vào Foodify",
},
{
title: "Về Pyramide Team",
desc: "Pyramide team là một nhóm sinh viên trường Đại học FPT, team nghiên cứu và phát triển sản phẩm theo công nghệ Angular và Spring Boot",
},
];
owlcarouselOptions = {
loop: true,
items: 1,
dots: true,
};
ngOnInit() {
this.sendEmailForm = this.formBuilder.group({
email: new FormControl("", [Validators.required, Validators.email]),
});
}
sendPasswordResetMail() {
if (this.sendEmailForm.invalid) {
this.sendEmailForm.markAllAsTouched();
return;
}
this.firebaseService.resetPassword(this.email.value);
this.layer1 = this.modalService.show(this.sendMailCompleted, { class: "modal-sm" });
}
gmail() {
this.layer1.hide();
window.location.assign("https://mail.google.com/");
}
closeLayer1() {
this.layer1.hide();
}
get email() {
return this.sendEmailForm.get("email");
}
}
@@ -0,0 +1,388 @@
<!-- Container-fluid starts-->
<div class="backgound-food" [ngClass]="{'change-height': active === 2}">
<div class="d-flex justify-content-center align-items-center">
<div class="form-shop-box">
<div class="d-flex justify-content-center align-items-center ">
<div class="card tab2-card form-shop-item">
<div class="card-header">
<h2 style="color: lightcoral" class="font-weight-bold">Đăng ký shop</h2>
</div>
<div class="card-body tab2-card">
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
<li [ngbNavItem]="1">
<a ngbNavLink>Tài khoản</a>
<ng-template ngbNavContent>
<form [formGroup]="addUserForm" class="needs-validation user-add" novalidate>
<h4>Chi tiết tài khoản</h4>
<!-- avatar -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom0"><span>*</span> Ảnh đại diện</label>
</div>
<div class="col-xl-8 col-md-7">
<div class="d-flex align-items-center">
<button *ngIf="!userImageChoosen" type="submit" class="btn btn-success"
(click)="chooseImg(chooseUserImage)">
Chọn ảnh
</button>
<button *ngIf="userImageChoosen" type="submit" class="btn btn-secondary"
(click)="chooseImg(chooseUserImage)">
Thay đổi ảnh
</button>
<img class="rounded-circle border border-dark" [src]="avatar" *ngIf="avatar"
style="width: 160px; height: 160px; margin-left: 20px" />
</div>
<!-- <div *ngIf="!userImageChoosen && (addUserForm.get('image')?.dirty || addUserForm.get('image')?.touched)" class="alert alert-danger mt-1">
<div *ngIf="addUserForm.get('image').errors?.['required']">Vui lòng chọn ảnh đại diện.</div>
</div> -->
</div>
</div>
<!-- full name -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom0"><span>*</span> Họ và tên</label>
</div>
<div class="col-xl-8 col-md-7">
<input formControlName="fullName" class="form-control" type="text" placeholder="Họ và tên" />
<div *ngIf="userFullName?.invalid && (userFullName?.dirty || userFullName?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="userFullName?.errors?.['required']">Vui lòng không được bỏ trống
tên.</div>
<div *ngIf="userFullName?.errors?.['minlength']">Tên phải có ít nhất 2 kí tự
</div>
</div>
</div>
</div>
<!-- email -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span> Email</label>
</div>
<div class="col-xl-8 col-md-7">
<input formControlName="email" class="form-control" type="email" placeholder="Email" />
<div *ngIf="userEmail?.invalid && (userEmail?.dirty || userEmail?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="userEmail?.errors?.['required']">Vui lòng không được bỏ trống
email.</div>
<div *ngIf="userEmail?.errors?.email">Email không hợp lệ, vui lòng nhập lại
</div>
</div>
</div>
</div>
<!-- date of birth -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span> Ngày Sinh</label>
</div>
<div class="col-xl-8 col-md-7">
<input type="date" formControlName="dateOfBirth" class="form-control" placeholder="Ngày sinh" />
<div *ngIf="userDateOfBirth?.invalid && (userDateOfBirth?.dirty || userDateOfBirth?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="userDateOfBirth?.errors?.['required']">Vui lòng không được bỏ
trống ngày sinh.</div>
</div>
</div>
</div>
<!-- phone number -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span> Số Điện Thoại</label>
</div>
<div class="col-xl-8 col-md-7">
<input formControlName="phoneNumber" class="form-control" type="tel"
placeholder="Số điện thoại" />
<div *ngIf="userPhoneNumber?.invalid && (userPhoneNumber?.dirty || userPhoneNumber?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="userPhoneNumber?.errors?.['required']">Vui lòng không được bỏ
trống số điện thoại.</div>
<div *ngIf="userPhoneNumber?.errors?.['pattern']">Số điện thoại phải có tối
đa 10 số</div>
</div>
</div>
</div>
<!-- identified code -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span> CMND/CCCD</label>
</div>
<div class="col-xl-8 col-md-7">
<input formControlName="identifiedCode" class="form-control" type="number"
placeholder="CMND/CCCD" />
<div
*ngIf="userIdentifiedCode?.invalid && (userIdentifiedCode?.dirty || userIdentifiedCode?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="userIdentifiedCode?.errors?.['required']">Vui lòng không được bỏ
trống CMND/CCCD.</div>
<div *ngIf="userIdentifiedCode?.errors?.['pattern']">CMND phải có tối đa 9
số hoặc CCCD phải có tối đa 12 số</div>
</div>
</div>
</div>
<hr>
<!-- Address -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span>Địa chỉ</label>
</div>
<div class="col-xl-8 col-md-7">
<input formControlName="address" class="form-control" type="text" placeholder="Địa chỉ" />
<div *ngIf="userAddress?.invalid && (userAddress?.dirty || userAddress?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="userAddress?.errors?.['required']">Vui lòng không được bỏ
trống địa chỉ</div>
<div *ngIf="userAddress?.errors?.['minlength']">Tên phải có ít nhất 8 kí tự</div>
<!-- <div *ngIf="userAddress?.errors?.['pattern']">CMND phải có tối đa 9
số hoặc CCCD phải có tối đa 12 số</div> -->
</div>
</div>
</div>
</form>
<div class="needs-validation user-add">
<!-- District -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span> Quận</label>
</div>
<div class="col-xl-8 col-md-7">
<select [(ngModel)]="district" class="form-control" (change)="onDistrictSelected()">
<option [selected]="true" [ngValue]="'none'">--- Chọn quận ---</option>
<option *ngFor="let district of districts" [ngValue]="district.name">{{
district.name }}
</option>
</select>
<div *ngIf="!validDistrict" class="alert alert-danger mt-1">
Vui lòng không được bỏ trống quận
</div>
</div>
</div>
<!-- Wards -->
<div *ngIf="isHaveDistrict" class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span> Phường</label>
</div>
<div class="col-xl-8 col-md-7">
<select [(ngModel)]="ward" class="form-control">
<option [ngValue]="'none'">--- Chọn phường ---</option>
<option *ngFor="let ward of wards">{{ ward.name }}
</option>
</select>
<div *ngIf="!validWard" class="alert alert-danger mt-1">
Vui lòng không được bỏ trống phường
</div>
</div>
</div>
</div>
<form [formGroup]="passwordForm" class="needs-validation user-add" novalidate>
<!-- Password -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span> Mật khẩu</label>
</div>
<div class="col-xl-8 col-md-7">
<div class="form-control input-password-holder">
<input formControlName="password" class="input-password"
type="{{ showPassword ? 'text' : 'password' }}" placeholder="Mật khẩu" />
<span class="icon-holder-eye" (click)="showPassword = !showPassword">
<i class="{{ showPassword ? 'fa fa-eye-slash' : 'fa fa-eye' }}"></i>
</span>
</div>
<div *ngIf="userPassword?.invalid && (userPassword?.dirty || userPassword?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="userPassword?.errors?.['required']">Vui lòng không được bỏ trống mật khẩu</div>
<div *ngIf="userPassword?.errors?.['pattern']">
Mật khẩu phải chứa ít nhất 8 ký tự, trong đó gồm một ký tự số, một
chữ hoa, một chữ thường và một ký
tự đặc biệt.
<br />
Mật khẩu không được chứa khoảng trắng.
</div>
</div>
</div>
</div>
<!-- Confirm Password -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span>Nhập lại mật khẩu</label>
</div>
<div class="col-xl-8 col-md-7">
<div class="form-control input-password-holder">
<input formControlName="confirmPassword" class="input-password"
type="{{ showPassword ? 'text' : 'password' }}" placeholder="Xác nhận mật khẩu" />
<span class="icon-holder-eye" (click)="showPassword = !showPassword">
<i class="{{ showPassword ? 'fa fa-eye-slash' : 'fa fa-eye' }}"></i>
</span>
</div>
<!-- <input formControlName="confirmPassword" class="form-control" type="text" placeholder="Xác nhận mật khẩu"/> -->
<div
*ngIf="userConfirmPassword?.invalid && (userConfirmPassword?.dirty || userConfirmPassword?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="userConfirmPassword?.errors?.['required']">Vui lòng không được bỏ trống mật khẩu
</div>
<div *ngIf="userConfirmPassword?.errors?.['pattern']">
Mật khẩu phải chứa ít nhất 8 ký tự, trong đó gồm một ký tự số, một
chữ hoa, một chữ thường và một ký
tự đặc biệt.
<br />
Mật khẩu không được chứa khoảng trắng.
</div>
<div *ngIf="userConfirmPassword?.errors?.['confirmedValidator']">
Mật khẩu xác nhận phải giống với mật khẩu đã nhập
</div>
</div>
</div>
</div>
</form>
</ng-template>
</li>
<!-- --------- SHOP ----------- -->
<li [ngbNavItem]="2">
<a ngbNavLink>Shop đăng ký</a>
<ng-template ngbNavContent>
<div class="row product-adding">
<div class="col-xl-5">
<div class="add-product">
<div class="row">
<div class="col-xl-9 xl-50 col-sm-6 col-9">
<img [src]="shopBanner" *ngIf="shopBanner" alt="" class="img-fluid image_zoom_1">
</div>
<!-- <div class="col-xl-3 xl-50 col-sm-6 col-3">
</div> -->
</div>
</div>
</div>
<div class="col-xl-7">
<form [formGroup]="shopForm" class="needs-validation user-add" novalidate="">
<!-- Shop Banner -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom0"><span>*</span> Banner shop</label>
</div>
<div class="col-xl-8 col-md-7">
<div class="d-flex align-items-center">
<button *ngIf="!shopImageChoosen" type="submit" class="btn btn-success"
(click)="chooseImg(chooseShopImage)">Chọn
ảnh</button>
<button *ngIf="shopImageChoosen" type="submit" class="btn btn-secondary"
(click)="chooseImg(chooseShopImage)">Thay đổi ảnh</button>
</div>
<!-- <div *ngIf="!shopImageChoosen && (shopForm.get('banner')?.dirty || shopForm.get('banner')?.touched)" class="alert alert-danger mt-1">
<div *ngIf="shopForm.get('banner').errors?.['required']">Vui lòng chọn ảnh banner của shop.</div>
</div> -->
<!-- <div *ngIf="imageUrl?.invalid && (imageUrl?.dirty || imageUrl?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="imageUrl?.errors?.['required']">Vui lòng chọn ảnh đại diện.
</div>
</div> -->
</div>
</div>
<!-- Shop Name -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span>Tên shop</label>
</div>
<div class="col-xl-8 col-md-7">
<input formControlName="name" class="form-control" type="text" placeholder="Tên shop" />
<div *ngIf="shopName?.invalid && (shopName?.dirty || shopName?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="shopName?.errors?.['required']">Vui lòng không được bỏ trống tên shop</div>
<div *ngIf="shopName?.errors?.['minlength']">Tên shop phải có ít nhất
2 kí tự</div>
</div>
</div>
</div>
<!-- Shop Description -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span>Mô tả shop</label>
</div>
<div class="col-xl-8 col-md-7">
<textarea formControlName="description" class="form-control" type="text"
placeholder="Mô tả về shop"></textarea>
<div
*ngIf="shopDescription?.invalid && (shopDescription?.dirty || shopDescription?.touched)"
class="alert alert-danger mt-1">
<div *ngIf="shopDescription?.errors?.['required']">Vui lòng không được bỏ
trống phần mô tả shop</div>
<div *ngIf="shopDescription?.errors?.['minlength']">Mô tả phải có ít nhất 8 kí tự</div>
</div>
</div>
</div>
<!-- Is Student Shop -->
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom2"><span>*</span> Loại shop</label>
</div>
<div class="col-xl-8 col-md-7">
<button *ngIf="isStudent" type="button" class="btn btn-secondary" (click)="swap()">Shop sinh
viên
</button>
<button *ngIf="!isStudent" type="button" class="btn btn-primary" (click)="swap()">Shop
thường
</button>
</div>
</div>
</form>
</div>
</div>
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="nav" class="mt-2"></div>
<div class="pull-right">
<button type="button" class="btn btn-danger mr-3" (click)="goBackToLogin()">Quay lại</button>
<button type="submit" class="btn btn-primary" (click)="createUser(createSuccess)">Tạo tài khoản</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
<ng-template #chooseUserImage>
<div class="modal-body text-center">
<p>Vui lòng chọn 1 ảnh</p>
<input class="input-image" type="file" accept="image/*" (change)="onFileSelected($event)" />
</div>
</ng-template>
<ng-template #chooseShopImage>
<div class="modal-body text-center">
<p>Vui lòng chọn 1 ảnh</p>
<input class="input-image" type="file" accept="image/*" (change)="onShopFileSelected($event)" />
</div>
</ng-template>
<ng-template #createSuccess>
<div class="modal-body text-center">
<p>Tạo tài khoản thành công. Vui lòng kiểm tra hòm thư để xác thực tài khoản</p>
<button type="button" class="btn btn-primary" (click)="continue()">Tiếp tục
</button>
</div>
</ng-template>
<ng-template #error_modal>
<div class="modal-body text-center">
<p>{{ errorItem }} đã tồn tại. Vui lòng chọn một {{ errorItem.toLowerCase() }} khác.</p>
<button type="button" class="btn btn-primary" (click)="closeLayer2()">Tiếp tục</button>
</div>
</ng-template>
@@ -0,0 +1,40 @@
.backgound-food {
background-image: url('../../../../assets/images/background-login.jpg');
width: 100vw;
height: 100%;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
z-index: -99;
}
.change-height {
height: 100vh;
}
.form-shop-box {
width: 1300px;
margin: 80px 0;
.form-shop-item {
width: 100%;
}
}
.input-password-holder {
display: flex;
justify-content: space-between;
align-items: center;
.input-password {
border: none;
width: 100%;
}
.icon-holder-eye {
color: #999a9b;
}
}
@@ -0,0 +1,474 @@
import { Component, TemplateRef, ViewChild } from "@angular/core";
import { AngularFireStorage } from "@angular/fire/compat/storage";
import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms";
import { Router } from "@angular/router";
import { BsModalRef, BsModalService } from "ngx-bootstrap/modal";
import { EMPTY, finalize, switchMap } from "rxjs";
import { Observable } from "rxjs-compat";
import { Validation } from "src/app/constants/Validation";
import { AuthService } from "src/app/shared/service/auth.service";
import { DistrictService } from "src/app/shared/service/district.service";
import { FirebaseService } from "src/app/shared/service/firebase.service";
import { ShopService } from "src/app/shared/service/shop.service";
import { UserService } from "src/app/shared/service/user.service";
import { Address } from "src/app/shared/tables/address";
import { District } from "src/app/shared/tables/district";
import { Shop } from "src/app/shared/tables/shop";
import { User } from "src/app/shared/tables/user";
import { Ward } from "src/app/shared/tables/ward";
import { environment } from "src/environments/environment";
@Component({
selector: "app-signup",
templateUrl: "./signup.component.html",
styleUrls: ["./signup.component.scss"],
})
export class SignupComponent {
// form
public active = 1;
addUserForm: FormGroup;
passwordForm: FormGroup;
shopForm: FormGroup;
// image
avatar: string;
shopBanner: string;
defaultUserImg = environment.userDefaultImg;
defaultShopImg = environment.shopDefaultImg;
// file
downloadURL: Observable<string>;
userImageFile: File;
shopImageFile: File;
imageLink;
// password
showPassword = false;
// address
isHaveDistrict: boolean;
district: string = "none";
ward: string = "none";
validDistrict = true;
validWard = true;
districts: District[];
wards: Ward[];
userImageChoosen: boolean = false;
shopImageChoosen: boolean = false;
isStudent: boolean = true;
//Modal
errorItem = '';
layer1: BsModalRef;
layer2: BsModalRef;
@ViewChild('error_modal') errorModal: TemplateRef<any>;
constructor(
private formBuilder: FormBuilder,
private authService: AuthService,
private userService: UserService,
private districtService: DistrictService,
private storage: AngularFireStorage,
private shopService: ShopService,
private modalService: BsModalService,
private router: Router,
private firebaseService: FirebaseService
) {
this.createUserForm();
this.createPasswordForm();
this.createShopForm();
}
createUserForm() {
this.addUserForm = this.formBuilder.group({
fullName: new FormControl("", [Validators.required, Validators.minLength(2)]),
email: new FormControl("", [Validators.required, Validators.email]),
dateOfBirth: new FormControl("", [Validators.required]),
phoneNumber: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.Phone)]),
identifiedCode: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.IdentifiedCode)]),
address: new FormControl("", [Validators.required, Validators.minLength(8)]),
});
}
createPasswordForm() {
this.passwordForm = this.formBuilder.group(
{
password: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.Password)]),
confirmPassword: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.Password)]),
},
{
validator: this.ConfirmedValidator("password", "confirmPassword"),
}
);
}
// Validation for password and confirm password
ConfirmedValidator(controlName: string, matchingControlName: string) {
return (formGroup: FormGroup) => {
const control = formGroup.controls[controlName];
const matchingControl = formGroup.controls[matchingControlName];
if (matchingControl.errors && !matchingControl.errors.confirmedValidator) {
return;
}
if (control.value !== matchingControl.value) {
matchingControl.setErrors({ confirmedValidator: true });
} else {
matchingControl.setErrors(null);
}
};
}
createShopForm() {
this.shopForm = this.formBuilder.group({
name: new FormControl("", [Validators.required, Validators.minLength(2)]),
description: new FormControl("", [Validators.required, Validators.minLength(8)]),
});
}
ngOnInit() {
this.getAllDistrict();
}
/**
* create User from Form
*/
createUser(template: TemplateRef<any>): Promise<void> {
const newUser = new User();
const newAddress = new Address();
const newShop = new Shop();
newUser.fullName = this.userFullName.value;
newUser.email = this.userEmail.value;
newUser.dateOfBirth = this.userDateOfBirth.value;
newUser.phoneNumber = this.userPhoneNumber.value;
newUser.identifiedCode = this.userIdentifiedCode.value;
newUser.defaultAddress = 0;
newUser.isLocked = false;
newUser.roleName = 'ROLE_SHOP';
if (this.district === 'none' || this.ward === 'none') {
this.validDistrict = this.district !== 'none';
this.validWard = this.ward !== 'none';
} else {
// Tiếp tục với quá trình tạo tài khoản
this.validDistrict = true;
this.validWard = true;
}
if (this.addUserForm.invalid || this.passwordForm.invalid || this.shopForm.invalid || this.district == 'none') {
this.addUserForm.markAllAsTouched();
this.passwordForm.markAllAsTouched();
this.shopForm.markAllAsTouched();
return Promise.reject();
}
newShop.name = this.shopName.value
newShop.description = this.shopDescription.value;
newShop.isStudent = this.isStudent;
newShop.isEnabled = true;
newShop.lat = '1';
newShop.lng = '1';
newAddress.address = this.userAddress.value;
newAddress.district = this.district;
if (this.district != "Huyện Hoàng Sa") newAddress.ward = this.ward;
this.authService.checkEmailOrPhoneNumberExist(newUser).subscribe((error) => {
if (error.title == 'emailExist') {
this.errorItem = 'Email';
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
return Promise.reject();
}
else if (error.title == 'phoneNumExist') {
this.errorItem = 'Số điện thoại';
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
return Promise.reject();
}
else {
if (this.userImageChoosen && this.shopImageChoosen) {
this.uploadUserImage(this.userImageFile).then((url) => {
newUser.imageUrl = url
this.authService.SignUpUser(newUser).pipe(
switchMap((user) => {
this.uploadShopImage(this.shopImageFile).then((url) => {
newShop.userId = user.id
newShop.imageUrl = url;
this.authService.SignUpUserAddress(user.id, newAddress).subscribe({
next: () => {
this.firebaseService.signUp(newUser.email, this.userPassword.value);
this.authService.SignUpShop(newShop).subscribe(() => {
this.layer1 = this.modalService.show(template, { class: 'modal-sm' })
})
}
});
})
return EMPTY; // Return an empty observable to prevent nested subscriptions
})
).subscribe({
next: () => { },
error: () => {
this.errorItem = 'CCCD/CMND';
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
}
})
})
}
else if (this.userImageChoosen && !this.shopImageChoosen) {
this.uploadUserImage(this.userImageFile).then((url) => {
newUser.imageUrl = url
this.authService.SignUpUser(newUser).pipe(
switchMap((user) => {
newShop.userId = user.id
newShop.imageUrl = this.defaultShopImg;
this.authService.SignUpUserAddress(user.id, newAddress).subscribe({
next: () => {
this.firebaseService.signUp(newUser.email, this.userPassword.value);
this.authService.SignUpShop(newShop).subscribe(() => {
this.layer1 = this.modalService.show(template, { class: 'modal-sm' })
})
}
});
return EMPTY; // Return an empty observable to prevent nested subscriptions
})
).subscribe({
next: () => { },
error: () => {
this.errorItem = 'CCCD/CMND';
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
}
})
})
}
else if (!this.userImageChoosen && this.shopImageChoosen) {
newUser.imageUrl = this.defaultUserImg;
this.authService.SignUpUser(newUser).pipe(
switchMap((user) => {
this.uploadShopImage(this.shopImageFile).then((url) => {
newShop.userId = user.id
newShop.imageUrl = url;
this.authService.SignUpUserAddress(user.id, newAddress).subscribe({
next: () => {
this.firebaseService.signUp(newUser.email, this.userPassword.value);
this.authService.SignUpShop(newShop).subscribe(() => {
this.layer1 = this.modalService.show(template, { class: 'modal-sm' })
})
}
});
})
return EMPTY; // Return an empty observable to prevent nested subscriptions
})
).subscribe({
next: () => { },
error: () => {
this.errorItem = 'CCCD/CMND';
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
}
})
}
else {
newUser.imageUrl = this.defaultUserImg;
this.authService.SignUpUser(newUser).pipe(
switchMap((user) => {
newShop.userId = user.id
newShop.imageUrl = this.defaultShopImg;
this.authService.SignUpUserAddress(user.id, newAddress).subscribe({
next: () => {
this.firebaseService.signUp(newUser.email, this.userPassword.value);
this.authService.SignUpShop(newShop).subscribe(() => {
this.layer1 = this.modalService.show(template, { class: 'modal-sm' })
})
}
});
return EMPTY; // Return an empty observable to prevent nested subscriptions
})
).subscribe({
next: () => { },
error: () => {
this.errorItem = 'CCCD/CMND';
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
}
})
}
}
})
}
//Districts and Wards
getAllDistrict() {
this.districtService.getAllDistricts().subscribe((distrs) => {
this.districts = distrs;
});
}
onDistrictSelected() {
this.isHaveDistrict = false;
this.districts.forEach((element: District) => {
if (this.district == element.name && this.district != "Huyện Hoàng Sa") {
this.isHaveDistrict = true;
this.wards = element.wards;
}
});
}
swap() {
if (this.isStudent) {
this.isStudent = false;
} else {
this.isStudent = true;
}
}
//Image Selected
onFileSelected(event) {
this.userImageChoosen = true;
this.userImageFile = event.target.files[0];
const reader = new FileReader();
if (event.target.files && event.target.files.length) {
const [file] = event.target.files;
reader.readAsDataURL(file);
reader.onload = () => {
this.avatar = reader.result as string;
localStorage.setItem("image", this.avatar);
};
}
this.layer1.hide();
}
//Shop Image Selected
onShopFileSelected(event) {
this.shopImageChoosen = true;
this.shopImageFile = event.target.files[0];
const reader = new FileReader();
if (event.target.files && event.target.files.length) {
const [file] = event.target.files;
reader.readAsDataURL(file);
reader.onload = () => {
this.shopBanner = reader.result as string;
localStorage.setItem("image", this.shopBanner);
};
}
this.layer1.hide();
}
uploadUserImage(fileUpload: File): Promise<string> {
return new Promise<string>((resolve) => {
let n = Date.now();
const filePath = `UserImages/${n}`;
const fileRef = this.storage.ref(filePath);
const task = this.storage.upload(`UserImages/${n}`, fileUpload);
task
.snapshotChanges()
.pipe(
finalize(() => {
this.downloadURL = fileRef.getDownloadURL();
this.downloadURL.subscribe((url) => {
if (url) {
resolve(url);
}
});
})
)
.subscribe((url) => { });
});
}
uploadShopImage(fileUpload: File): Promise<string> {
return new Promise<string>((resolve) => {
let n = Date.now();
const filePath = `Shops/${n}`;
const fileRef = this.storage.ref(filePath);
const task = this.storage.upload(`Shops/${n}`, fileUpload);
task
.snapshotChanges()
.pipe(
finalize(() => {
this.downloadURL = fileRef.getDownloadURL();
this.downloadURL.subscribe((url) => {
if (url) {
resolve(url);
}
});
})
)
.subscribe((url) => { });
});
}
chooseImg(template: TemplateRef<any>) {
this.layer1 = this.modalService.show(template, { class: "modal-sm" });
}
continue() {
this.layer1.hide();
this.router.navigate(["/vendors/list"]);
}
closeLayer2() {
this.layer2.hide();
}
goBackToLogin() {
this.router.navigate(['/auth/login']);
}
//Getter
get image() {
return this.addUserForm.get("image");
}
get userFullName() {
return this.addUserForm.get("fullName");
}
get userEmail() {
return this.addUserForm.get("email");
}
get userDateOfBirth() {
return this.addUserForm.get("dateOfBirth");
}
get userPhoneNumber() {
return this.addUserForm.get("phoneNumber");
}
get userIdentifiedCode() {
return this.addUserForm.get("identifiedCode");
}
get userAddress() {
return this.addUserForm.get("address");
}
get userDistrict() {
return this.addUserForm.get("district");
}
get userWard() {
return this.addUserForm.get("ward");
}
get userPassword() {
return this.passwordForm.get("password");
}
get userConfirmPassword() {
return this.passwordForm.get("confirmPassword");
}
//Shop
get banner() {
return this.shopForm.get("banner");
}
get shopName() {
return this.shopForm.get("name");
}
get shopDescription() {
return this.shopForm.get("description");
}
}
@@ -0,0 +1,34 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ListCouponComponent } from './list-coupon/list-coupon.component';
import { CreateCouponComponent } from './create-coupon/create-coupon.component';
const routes: Routes = [
{
path: '',
children: [
{
path: 'list-coupons',
component: ListCouponComponent,
data: {
title: "List Coupons",
breadcrumb: "List Coupons"
}
},
{
path: 'create-coupons',
component: CreateCouponComponent,
data: {
title: "Create Coupon",
breadcrumb: "Create Coupons"
}
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class CouponsRoutingModule { }
@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CouponsRoutingModule } from './coupons-routing.module';
import { ListCouponComponent } from './list-coupon/list-coupon.component';
import { CreateCouponComponent } from './create-coupon/create-coupon.component';
import { SharedModule } from 'src/app/shared/shared.module';
@NgModule({
declarations: [ListCouponComponent, CreateCouponComponent],
imports: [
CommonModule,
CouponsRoutingModule,
NgbModule,
ReactiveFormsModule,
SharedModule,
FormsModule
]
})
export class CouponsModule { }
@@ -0,0 +1,184 @@
<!-- Container-fluid starts-->
<div class="container-fluid">
<div class="card">
<div class="card-header">
<h5>Discount Coupon Details</h5>
</div>
<div class="card-body tab2-card">
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
<li [ngbNavItem]="1">
<a ngbNavLink>General</a>
<ng-template ngbNavContent>
<form [formGroup]="generalForm" class="needs-validation" novalidate="">
<h4>General</h4>
<div class="row">
<div class="col-sm-12">
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom0"><span>*</span>
Coupan Title</label>
</div>
<div class="col-md-7">
<input class="form-control" formControlName="name"
id="validationCustom0" type="text" required="">
</div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom1"><span>*</span>
Coupan Code</label>
</div>
<div class="col-md-7">
<input class="form-control" formControlName="code"
id="validationCustom1" type="text" required="">
</div>
<div class="valid-feedback">Please Provide a Valid Coupon Code.</div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label>Start Date</label>
</div>
<div class="col-md-7 ">
<div class="input-group input-grp-p">
<input class="datepicker-here form-control digits" ngbDatepicker
formControlName="start_date" #f="ngbDatepicker">
<div class="input-group-append">
<button class="btn calendar" (click)="f.toggle()" type="button"><i
class="fa fa-calendar"></i></button>
</div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label>End Date</label>
</div>
<div class="col-md-7">
<div class="input-group input-grp-p">
<input class="datepicker-here form-control digits" ngbDatepicker
formControlName="end_date" #c="ngbDatepicker">
<div class="input-group-append">
<button class="btn calendar" (click)="c.toggle()" type="button"><i
class="fa fa-calendar"></i></button>
</div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label>Free Shipping</label>
</div>
<div class="col-md-7">
<div class="checkbox checkbox-primary">
<input id="checkbox-primary-1" type="checkbox"
formControlName="free_shipping" data-original-title="" title="">
<label for="checkbox-primary-1">Allow Free Shipping</label>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4"><label>Quantity</label></div>
<div class="col-md-7">
<input class="form-control" formControlName="quantity" type="number"
required="">
</div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4"><label>Discount Type</label></div>
<div class="col-md-7">
<select class="custom-select form-select" required="">
<option value="">--Select--</option>
<option value="1">Percent</option>
<option value="2">Fixed</option>
</select>
</div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label>Status</label>
</div>
<div class="col-md-7">
<div class="checkbox checkbox-primary">
<input id="checkbox-primary-2" formControlName="status" type="checkbox"
data-original-title="" title="">
<label for="checkbox-primary-2">Enable the Coupon</label>
</div>
</div>
</div>
</div>
</div>
</form>
</ng-template>
</li>
<li [ngbNavItem]="2">
<a ngbNavLink>Restriction</a>
<ng-template ngbNavContent>
<form [formGroup]="restrictionForm" class="needs-validation" novalidate="">
<h4>Restriction</h4>
<div class="form-group row">
<div class="col-xl-3 col-md-4"><label for="validationCustom3">Products</label></div>
<div class="col-md-7"><input class="form-control" formControlName="products" id="validationCustom3"
type="text" required="">
</div>
<div class="valid-feedback">Please Provide a Product Name.</div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4"><label>Category</label></div>
<div class="col-md-7">
<select class="custom-select form-select" formControlName="category" required="">
<option value="">--Select--</option>
<option value="1">Electronics</option>
<option value="2">Clothes</option>
<option value="2">Shoes</option>
<option value="2">Digital</option>
</select>
</div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4"><label for="validationCustom4">Minimum Spend</label></div>
<div class="col-md-7">
<input class="form-control" formControlName="min" id="validationCustom4"
type="number">
</div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4">
<label for="validationCustom5">Maximum Spend</label>
</div>
<div class="col-md-7">
<input class="form-control" formControlName="max" id="validationCustom5"
type="number">
</div>
</div>
</form>
</ng-template>
</li>
<li [ngbNavItem]="3">
<a ngbNavLink>Usage</a>
<ng-template ngbNavContent>
<form [formGroup]="usageForm" class="needs-validation" novalidate="">
<h4>Usage Limits</h4>
<div class="form-group row">
<div class="col-xl-3 col-md-4"><label for="validationCustom6">Per Limit</label></div>
<div class="col-md-7"><input class="form-control" formControlName="limit" id="validationCustom6"
type="number">
</div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4"><label for="validationCustom7">Per Customer</label></div>
<div class="col-md-7"><input class="form-control" formControlName="customer" id="validationCustom7"
type="number">
</div>
</div>
</form>
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="nav" class="mt-2"></div>
<div class="pull-right">
<button type="button" class="btn btn-primary">Save</button>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,61 @@
import { Component, OnInit } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { NgbDateStruct, NgbDate, NgbCalendar, NgbDatepickerConfig } from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'app-create-coupon',
templateUrl: './create-coupon.component.html',
styleUrls: ['./create-coupon.component.scss']
})
export class CreateCouponComponent implements OnInit {
public generalForm: UntypedFormGroup;
public restrictionForm: UntypedFormGroup;
public usageForm: UntypedFormGroup;
public model: NgbDateStruct;
public date: { year: number, month: number };
public modelFooter: NgbDateStruct;
public active = 1;
constructor(private formBuilder: UntypedFormBuilder, private calendar: NgbCalendar) {
this.createGeneralForm();
this.createRestrictionForm();
this.createUsageForm();
}
selectToday() {
this.model = this.calendar.getToday();
}
createGeneralForm() {
this.generalForm = this.formBuilder.group({
name: [''],
code: [''],
start_date: [''],
end_date: [''],
free_shipping: [''],
quantity: [''],
discount_type: [''],
status: [''],
});
}
createRestrictionForm() {
this.restrictionForm = this.formBuilder.group({
products: [''],
category: [''],
min: [''],
max: ['']
})
}
createUsageForm() {
this.usageForm = this.formBuilder.group({
limit: [''],
customer: ['']
})
}
ngOnInit() {
}
}
@@ -0,0 +1,48 @@
<!-- Container-fluid starts-->
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-header">
<h5>Products Category</h5>
</div>
<div class="card-body">
<div class="custom-datatable">
<button class="btn btn-primary me-1 mb-3" style="float: right;">Delete</button>
<div class="clearfix"></div>
<table class="table table-striped">
<thead>
<tr>
<th scope="col" sortable="title" (sort)="onSort($event)">Title</th>
<th scope="col" sortable="code" (sort)="onSort($event)">Code</th>
<th scope="col" sortable="discount" (sort)="onSort($event)">Discoount</th>
<th scope="col" sortable="status" (sort)="onSort($event)">Status</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of tableItem$ | async ">
<th scope="row">
<input type="checkbox" name="title_select" onclick="onSelect(item.id)">
{{ item.title }}
</th>
<td>
{{item.code}}
</td>
<td>{{item.discount}}</td>
<td><i class="fa fa-circle {{item.status}} f-12"></i></td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-center p-2">
<ngb-pagination
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
</ngb-pagination>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,51 @@
import {DecimalPipe} from '@angular/common';
import {Component, OnInit, QueryList, ViewChildren} from '@angular/core';
import {Observable} from 'rxjs';
import {NgbdSortableHeader, SortEvent} from 'src/app/shared/directives/NgbdSortableHeader';
import {TableService} from 'src/app/shared/service/table.service';
import {LISTCOUPLEDB, ListCouponsDB} from 'src/app/shared/tables/list-coupon';
@Component({
selector: 'app-list-coupon',
templateUrl: './list-coupon.component.html',
styleUrls: ['./list-coupon.component.scss'],
providers: [TableService, DecimalPipe]
})
export class ListCouponComponent implements OnInit {
public selected = [];
public tableItem$: Observable<ListCouponsDB[]>;
public searchText;
total$: Observable<number>;
constructor(public service: TableService) {
this.tableItem$ = service.tableItem$;
this.total$ = service.total$;
this.service.setUserData(LISTCOUPLEDB);
}
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
onSort({column, direction}: SortEvent) {
// resetting other headers
this.headers.forEach((header) => {
if (header.sortable !== column) {
header.direction = '';
}
});
this.service.sortColumn = column;
this.service.sortDirection = direction;
}
onSelect({selected}) {
this.selected.splice(0, this.selected.length);
this.selected.push(...selected);
}
ngOnInit() {
}
}
@@ -0,0 +1,28 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { DashboardComponent } from './dashboard.component';
import { AuthGuard } from 'src/app/shared/guard/auth-guard.service';
const routes: Routes = [
{
path: '',
children: [
{
path: 'default',
component: DashboardComponent,
canActivate: [AuthGuard],
data: {
title: "Trang chủ",
breadcrumb: "Trang chủ"
}
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class DashboardRoutingModule { }
@@ -0,0 +1,726 @@
<!-- Container-fluid starts-->
<div class="container-fluid">
<div class="row">
<div class="col-xl-3 col-md-6 xl-50">
<div class="card o-hidden widget-cards">
<div class="bg-warning card-body">
<div class="media static-top-widget row">
<div class="icons-widgets col-4">
<div class="align-self-center text-center">
<app-feather-icons *ngIf="isShop" [icon]="'dollar-sign'"
class="font-warning"></app-feather-icons>
<app-feather-icons *ngIf="!isShop" [icon]="'user'"
class="font-warning"></app-feather-icons>
</div>
</div>
<div *ngIf="isShop" class="media-body col-8"><span class="m-0">Thu nhập 30 ngày qua</span>
<h3 class="mb-0">
<span class="counter" [CountTo]="totalRevenue" [from]="0" [duration]="2">{{ totalRevenue
| currency : "VND"
}}</span>
</h3>
</div>
<div *ngIf="!isShop" class="media-body col-8"><span class="m-0">Người dùng đăng ký mới</span>
<h3 class="mb-0">
<span class="counter" [CountTo]="totalUser" [from]="0" [duration]="2">{{ totalUser
}}</span><small>
người dùng</small>
</h3>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 xl-50">
<div class="card o-hidden widget-cards">
<div class="bg-secondary card-body">
<div class="media static-top-widget row">
<div class="icons-widgets col-4">
<div class="align-self-center text-center">
<app-feather-icons [icon]="'package'" class="font-secondary"></app-feather-icons>
</div>
</div>
<div class="media-body col-8"><span class="m-0">Tổng sản phẩm đã bán</span>
<h3 class="mb-0"><span class="counter" [CountTo]="totalProducts" [from]="0"
[duration]="2">{{ totalProducts }}</span><small> sản phẩm</small></h3>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 xl-50">
<div class="card o-hidden widget-cards">
<div class="bg-primary card-body">
<div class="media static-top-widget row">
<div class="icons-widgets col-4">
<div class="align-self-center text-center">
<app-feather-icons [icon]="'message-square'" class="font-primary"></app-feather-icons>
</div>
</div>
<div class="media-body col-8"><span class="m-0">Số lượt đánh giá sản phẩm</span>
<h3 class="mb-0"><span class="counter" [CountTo]="totalReviewCount" [from]="0"
[duration]="2">{{ totalReviewCount }}</span><small> lượt</small></h3>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 xl-50">
<div class="card o-hidden widget-cards">
<div class="bg-danger card-body">
<div class="media static-top-widget row">
<div class="icons-widgets col-4">
<div class="align-self-center text-center">
<app-feather-icons [icon]="'star'" class="font-danger"></app-feather-icons>
</div>
</div>
<div class="media-body col-8"><span class="m-0">Đánh giá trung bình</span>
<h3 class="mb-0"><span class="counter" [CountTo]="totalAverageRating" [from]="0"
[duration]="2">{{ totalAverageRating }}</span><small> /5</small></h3>
</div>
</div>
</div>
</div>
</div>
<!-- <div class="col-xl-6 xl-100">-->
<!-- <div class="card height-equal">-->
<!-- <div class="card-header">-->
<!-- <h5>Market Value</h5>-->
<!-- </div>-->
<!-- <div class="card-body">-->
<!-- <div class="market-chart">-->
<!-- <x-chartist class="board-chart ct-golden-section" [data]="chart3.data" [type]="chart3.type"-->
<!-- [options]="chart3.options" [events]="chart3.events">-->
<!-- </x-chartist>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<div class="col-xl-12 xl-100">
<div class="card height-equal">
<div class="card-header">
<h5 *ngIf="isShop">Đơn hàng của bạn</h5>
<h5 *ngIf="!isShop">Các đơn hàng gần đây</h5>
</div>
<div class="card-body" *ngIf="orders.length">
<div class=" user-status table-responsive latest-order-table">
<table class="table table-bordernone">
<thead>
<tr>
<th scope="col">Mã đơn hàng</th>
<th scope="col">Tổng giá trị đơn hàng</th>
<th scope="col">Phương thức thanh toán</th>
<th scope="col">Trạng thái đơn hàng</th>
<th scope="col">Địa chỉ</th>
<th scope="col">Shipper</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let order of orders; let i = index">
<td>{{ order?.orderTrackingNumber }}</td>
<td>{{ order?.total | currency : "VND" }}</td>
<td><span [ngClass]="{
badge: true,
'badge-info': order.paymentMethod === 'ZALO PAY',
'badge-success': order.paymentMethod === 'CASH'
}">
{{ order.paymentMethod === "CASH"
? "Tiền mặt"
: order.paymentMethod === "ZALO PAY"
? "Zalo pay"
: ""
}}
</span></td>
<td><span [ngClass]="{
badge: true,
'badge-dark': order.status === 'AWAITING',
'badge-warning': order.status === 'CONFIRMED',
'badge-info': order.status === 'SHIPPING',
'badge-success': order.status === 'COMPLETED',
'badge-primary': order.status === 'CANCELED' || order.status === 'REJECT_DELIVERY'
}">
{{
order.status === "AWAITING"
? "Chờ xác nhận"
: order.status === "CONFIRMED"
? "Đã xác nhận"
: order.status === "SHIPPING"
? "Đang giao hàng"
: order.status === "COMPLETED"
? "Giao thành công"
: order.status === "CANCELED"
? "Đã huỷ đơn"
: order.status === "REJECT_DELIVERY"
? "Không nhận hàng"
: ""
}}
</span></td>
<td>{{ order?.address }}</td>
<td *ngIf="order?.shipper?.user?.fullName != null">{{ order?.shipper?.user?.fullName
}}</td>
<td *ngIf="order?.shipper?.user?.fullName == null"><strong>Đang chờ...</strong></td>
<td>
<a [routerLink]="['/sales', 'detail-order', 'user', order.user.id, 'order', order.id]"
style="cursor: pointer;">
<i style="color: lightseagreen;" class="fa fa-eye f-12"></i>
</a>&nbsp;
</td>
</tr>
<!-- <tr>
<td>2</td>
<td class="digits">$90.00</td>
<td class="font-secondary">Ewallets</td>
<td class="digits">Delivered</td>
</tr>
<tr>
<td>3</td>
<td class="digits">$240.00</td>
<td class="font-warning">Cash</td>
<td class="digits">Delivered</td>
</tr>
<tr>
<td>4</td>
<td class="digits">$120.00</td>
<td class="font-primary">Direct Deposit</td>
<td class="digits">$6523</td>
</tr>
<tr>
<td>5</td>
<td class="digits">$50.00</td>
<td class="font-primary">Bank Transfers</td>
<td class="digits">Delivered</td>
</tr> -->
</tbody>
</table>
<a [routerLink]="['/sales', 'orders']" class="btn btn-primary">Xem tất cả đơn hàng</a>
</div>
</div>
<div class=" user-status table-responsive latest-order-table" *ngIf="!orders.length">
<div class="d-flex justify-content-center p-2">
<img src="../../../assets/images/error/no-order.png" alt="" srcset="">
</div>
<div class="d-flex justify-content-center p-2">
<h4>Bạn chưa có đơn hàng nào</h4>
</div>
</div>
</div>
</div>
<!-- <div class="col-xl-3 col-md-6 xl-50">
<div class="card order-graph sales-carousel">
<div class="card-header">
<h6>Total Sales</h6>
<div class="row">
<div class="col-6">
<div class="small-chartjs">
<canvas baseChart id="small-graph-1" height="600" width="400"
[datasets]="smallLineChartData" [labels]="smallLineChartLabels"
[options]="smallLineChartOptions" [legend]="smallLineChartLegend"
[type]="smallLineChartType" (chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>
<div class="col-6">
<div class="value-graph">
<h3>42% <span><i class="fa fa-angle-up font-primary"></i></span></h3>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="d-flex align-items-start">
<div class="media-body">
<span>Sales Last Month</span>
<h2 class="mb-0">9054</h2>
<p>0.25% <span><i class="fa fa-angle-up"></i></span></p>
<h5 class="f-w-600">Gross sales of August</h5>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting</p>
</div>
<div class="bg-primary b-r-8">
<div class="small-box">
<i data-feather="briefcase"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 xl-50">
<div class="card order-graph sales-carousel">
<div class="card-header">
<h6>Total purchase</h6>
<div class="row">
<div class="col-6">
<div class="small-chartjs">
<canvas baseChart id="small-graph-2" height="60vw" width="140vw"
[datasets]="smallLine2ChartData" [labels]="smallLine2ChartLabels"
[options]="smallLine2ChartOptions" [legend]="smallLine2ChartLegend"
[type]="smallLine2ChartType" (chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>
<div class="col-6">
<div class="value-graph">
<h3>20% <span><i class="fa fa-angle-up font-secondary"></i></span></h3>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="d-flex align-items-start">
<div class="media-body">
<span>Monthly purchase</span>
<h2 class="mb-0">2154</h2>
<p>0.13% <span><i class="fa fa-angle-up"></i></span></p>
<h5 class="f-w-600">Avg Gross purchase</h5>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting</p>
</div>
<div class="bg-secondary b-r-8">
<div class="small-box">
<i data-feather="credit-card"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 xl-50">
<div class="card order-graph sales-carousel">
<div class="card-header">
<h6>Total cash transaction</h6>
<div class="row">
<div class="col-6">
<div class="small-chartjs">
<canvas baseChart id="small-graph-3" height="60vw" width="140vw"
[datasets]="smallLine3ChartData" [labels]="smallLine3ChartLabels"
[options]="smallLine3ChartOptions" [legend]="smallLine3ChartLegend"
[type]="smallLine3ChartType" (chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>
<div class="col-6">
<div class="value-graph">
<h3>28% <span><i class="fa fa-angle-up font-warning"></i></span></h3>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="d-flex align-items-start">
<div class="media-body">
<span>Cash on hand</span>
<h2 class="mb-0">4672</h2>
<p>0.8% <span><i class="fa fa-angle-up"></i></span></p>
<h5 class="f-w-600">Details about cash</h5>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting</p>
</div>
<div class="bg-warning b-r-8">
<div class="small-box">
<i data-feather="shopping-cart"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 xl-50">
<div class="card order-graph sales-carousel">
<div class="card-header">
<h6>Daily Deposits</h6>
<div class="row">
<div class="col-6">
<div class="small-chartjs">
<canvas baseChart id="small-graph-4" height="60vw" width="140vw"
[datasets]="smallLine4ChartData" [labels]="smallLine4ChartLabels"
[options]="smallLine4ChartOptions" [legend]="smallLine4ChartLegend"
[type]="smallLine4ChartType" (chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>
<div class="col-6">
<div class="value-graph">
<h3>75% <span><i class="fa fa-angle-up font-danger"></i></span></h3>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="d-flex align-items-start">
<div class="media-body">
<span>Security Deposits</span>
<h2 class="mb-0">0782</h2>
<p>0.25% <span><i class="fa fa-angle-up"></i></span></p>
<h5 class="f-w-600">Gross sales of June</h5>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting</p>
</div>
<div class="bg-danger b-r-8">
<div class="small-box">
<i data-feather="calendar"></i>
</div>
</div>
</div>
</div>
</div>
</div> -->
<!-- <div class="col-sm-12">
<div class="card">
<div class="card-header">
<h5>Buy / Sell</h5>
</div>
<div class="card-body sell-graph">
<canvas baseChart id="myGraph" [datasets]="lineChartData" [labels]="lineChartLabels"
[options]="lineChartOptions" [legend]="lineChartLegend" [type]="lineChartType"
(chartHover)="chartHovered($event)" (chartClick)="chartClicked($event)"></canvas>
</div>
</div>
</div> -->
<!-- <div class="col-xl-12 xl-100">-->
<!-- <div class="card height-equal">-->
<!-- <div class="card-header">-->
<!-- <h5>Products Cart</h5>-->
<!-- </div>-->
<!-- <div class="card-body">-->
<!-- <div class="user-status table-responsive products-table">-->
<!-- <table class="table table-bordernone mb-0">-->
<!-- <thead>-->
<!-- <tr>-->
<!-- <th scope="col">Details</th>-->
<!-- <th scope="col">Quantity</th>-->
<!-- <th scope="col">Status</th>-->
<!-- <th scope="col">Price</th>-->
<!-- </tr>-->
<!-- </thead>-->
<!-- <tbody>-->
<!-- <tr>-->
<!-- <td>Simply dummy text of the printing</td>-->
<!-- <td class="digits">1</td>-->
<!-- <td class="font-primary">Pending</td>-->
<!-- <td class="digits">$6523</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td>Long established</td>-->
<!-- <td class="digits">5</td>-->
<!-- <td class="font-secondary">Cancle</td>-->
<!-- <td class="digits">$6523</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td>sometimes by accident</td>-->
<!-- <td class="digits">10</td>-->
<!-- <td class="font-secondary">Cancle</td>-->
<!-- <td class="digits">$6523</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td>Classical Latin literature</td>-->
<!-- <td class="digits">9</td>-->
<!-- <td class="font-primary">Return</td>-->
<!-- <td class="digits">$6523</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td>keep the site on the Internet</td>-->
<!-- <td class="digits">8</td>-->
<!-- <td class="font-primary">Pending</td>-->
<!-- <td class="digits">$6523</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td>Molestiae consequatur</td>-->
<!-- <td class="digits">3</td>-->
<!-- <td class="font-secondary">Cancle</td>-->
<!-- <td class="digits">$6523</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td>Pain can procure</td>-->
<!-- <td class="digits">8</td>-->
<!-- <td class="font-primary">Return</td>-->
<!-- <td class="digits">$6523</td>-->
<!-- </tr>-->
<!-- </tbody>-->
<!-- </table>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="col-xl-6 xl-100">-->
<!-- <div class="card height-equal">-->
<!-- <div class="card-header">-->
<!-- <h5>Empolyee Status</h5>-->
<!-- </div>-->
<!-- <div class="card-body">-->
<!-- <div class="user-status table-responsive products-table">-->
<!-- <table class="table table-bordernone mb-0">-->
<!-- <thead>-->
<!-- <tr>-->
<!-- <th scope="col">Name</th>-->
<!-- <th scope="col">Designation</th>-->
<!-- <th scope="col">Skill Level</th>-->
<!-- <th scope="col">Experience</th>-->
<!-- </tr>-->
<!-- </thead>-->
<!-- <tbody>-->
<!-- <tr>-->
<!-- <td class="bd-t-none u-s-tb">-->
<!-- <div class="align-middle image-sm-size"><img-->
<!-- class="img-radius align-top m-r-15 rounded-circle"-->
<!-- src="assets/images/dashboard/user2.jpg" alt="" data-original-title=""-->
<!-- title="">-->
<!-- <div class="d-inline-block">-->
<!-- <h6>John Deo <span class="text-muted digits">(14+ Online)</span></h6>-->
<!-- </div>-->
<!-- </div>-->
<!-- </td>-->
<!-- <td>Designer</td>-->
<!-- <td>-->
<!-- <div class="progress-showcase">-->
<!-- <div class="progress" style="height: 8px;">-->
<!-- <div class="progress-bar bg-primary" role="progressbar"-->
<!-- style="width: 30%" aria-valuenow="50" aria-valuemin="0"-->
<!-- aria-valuemax="100"></div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </td>-->
<!-- <td class="digits">2 Year</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td class="bd-t-none u-s-tb">-->
<!-- <div class="align-middle image-sm-size"><img-->
<!-- class="img-radius align-top m-r-15 rounded-circle"-->
<!-- src="assets/images/dashboard/user1.jpg" alt="" data-original-title=""-->
<!-- title="">-->
<!-- <div class="d-inline-block">-->
<!-- <h6>Holio Mako <span class="text-muted digits">(250+ Online)</span></h6>-->
<!-- </div>-->
<!-- </div>-->
<!-- </td>-->
<!-- <td>Developer</td>-->
<!-- <td>-->
<!-- <div class="progress-showcase">-->
<!-- <div class="progress" style="height: 8px;">-->
<!-- <div class="progress-bar bg-secondary" role="progressbar"-->
<!-- style="width: 70%" aria-valuenow="50" aria-valuemin="0"-->
<!-- aria-valuemax="100"></div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </td>-->
<!-- <td class="digits">3 Year</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td class="bd-t-none u-s-tb">-->
<!-- <div class="align-middle image-sm-size"><img-->
<!-- class="img-radius align-top m-r-15 rounded-circle"-->
<!-- src="assets/images/dashboard/man.png" alt="" data-original-title=""-->
<!-- title="">-->
<!-- <div class="d-inline-block">-->
<!-- <h6>Mohsib lara<span class="text-muted digits">(99+ Online)</span></h6>-->
<!-- </div>-->
<!-- </div>-->
<!-- </td>-->
<!-- <td>Tester</td>-->
<!-- <td>-->
<!-- <div class="progress-showcase">-->
<!-- <div class="progress" style="height: 8px;">-->
<!-- <div class="progress-bar bg-primary" role="progressbar"-->
<!-- style="width: 60%" aria-valuenow="50" aria-valuemin="0"-->
<!-- aria-valuemax="100"></div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </td>-->
<!-- <td class="digits">5 Month</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td class="bd-t-none u-s-tb">-->
<!-- <div class="align-middle image-sm-size"><img-->
<!-- class="img-radius align-top m-r-15 rounded-circle"-->
<!-- src="assets/images/dashboard/user.png" alt="" data-original-title=""-->
<!-- title="">-->
<!-- <div class="d-inline-block">-->
<!-- <h6>Hileri Soli <span class="text-muted digits">(150+ Online)</span>-->
<!-- </h6>-->
<!-- </div>-->
<!-- </div>-->
<!-- </td>-->
<!-- <td>Designer</td>-->
<!-- <td>-->
<!-- <div class="progress-showcase">-->
<!-- <div class="progress" style="height: 8px;">-->
<!-- <div class="progress-bar bg-secondary" role="progressbar"-->
<!-- style="width: 30%" aria-valuenow="50" aria-valuemin="0"-->
<!-- aria-valuemax="100"></div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </td>-->
<!-- <td class="digits">3 Month</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td class="bd-t-none u-s-tb">-->
<!-- <div class="align-middle image-sm-size"><img-->
<!-- class="img-radius align-top m-r-15 rounded-circle"-->
<!-- src="assets/images/dashboard/designer.jpg" alt=""-->
<!-- data-original-title="" title="">-->
<!-- <div class="d-inline-block">-->
<!-- <h6>Pusiz bia <span class="text-muted digits">(14+ Online)</span></h6>-->
<!-- </div>-->
<!-- </div>-->
<!-- </td>-->
<!-- <td>Designer</td>-->
<!-- <td>-->
<!-- <div class="progress-showcase">-->
<!-- <div class="progress" style="height: 8px;">-->
<!-- <div class="progress-bar bg-primary" role="progressbar"-->
<!-- style="width: 90%" aria-valuenow="50" aria-valuemin="0"-->
<!-- aria-valuemax="100"></div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </td>-->
<!-- <td class="digits">5 Year</td>-->
<!-- </tr>-->
<!-- </tbody>-->
<!-- </table>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<div class="col-sm-12">
<div class="card">
<div class="card-header">
<h5>Tổng quan đơn hàng</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-xl-6 col-sm-6 xl-50">
<div class="order-graph overflow-hidden">
<h6>Vị trí đặt đơn</h6>
<div class="chart-block chart-vertical-center">
<ngx-charts-pie-chart class="pie-colours-1" [view]="view"
[scheme]="doughnutChartColorScheme" [results]="pieData" [explodeSlices]="false"
[labels]="doughnutChartShowLabels" [arcWidth]=0.50 [doughnut]="true"
[animations]="false" [tooltipDisabled]="doughnutChartTooltip"
[gradient]="doughnutChartGradient">
</ngx-charts-pie-chart>
</div>
<div class="order-graph-bottom">
<div class="media" *ngIf="thanhKheValue > 0">
<div class="order-color-primary"></div>
<div class="media-body">
<h6 class="mb-0">Quận Thanh Khê <span class="pull-right">{{ thanhKheValue
}} đơn hàng</span></h6>
</div>
</div>
<div class="media" *ngIf="sonTraValue > 0">
<div class="order-color-secondary"></div>
<div class="media-body">
<h6 class="mb-0">Quận Sơn Trà <span class="pull-right">{{ sonTraValue
}} đơn hàng</span></h6>
</div>
</div>
<div class="media" *ngIf="camLeValue > 0">
<div class="order-color-danger"></div>
<div class="media-body">
<h6 class="mb-0">Quận Cẩm Lệ<span class="pull-right">{{ camLeValue }} đơn
hàng</span></h6>
</div>
</div>
<div class="media" *ngIf="lienChieuValue > 0">
<div class="order-color-warning"></div>
<div class="media-body">
<h6 class="mb-0">Quận Liên Chiểu<span class="pull-right">{{ lienChieuValue
}} đơn hàng</span></h6>
</div>
</div>
<div class="media" *ngIf="haiChauValue > 0">
<div class="order-color-success" style="background-color: #32CD32;"></div>
<div class="media-body">
<h6 class="mb-0">Quận Hải Châu<span class="pull-right">{{ haiChauValue }}
đơn hàng</span></h6>
</div>
</div>
<div class="media" *ngIf="hoaVangValue > 0">
<div class="order-color-success" style="background-color: mediumpurple;"></div>
<div class="media-body">
<h6 class="mb-0">Huyện Hoà Vang<span class="pull-right">{{ hoaVangValue }}
đơn hàng</span></h6>
</div>
</div>
<div class="media" *ngIf="nguHanhSonValue > 0">
<div class="order-color-success" style="background-color: deepskyblue;"></div>
<div class="media-body">
<h6 class="mb-0">Quận Ngũ Hành Sơn<span class="pull-right">{{
nguHanhSonValue }} đơn hàng</span></h6>
</div>
</div>
<div class="media" *ngIf="hoangSaValue > 0">
<div class="order-color-success" style="background-color: olive;"></div>
<div class="media-body">
<h6 class="mb-0">Huyện Hoàng Sa<span class="pull-right">{{ hoangSaValue }}
đơn hàng</span></h6>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-6 col-sm-6 xl-50">
<div class="order-graph sm-order-space overflow-hidden">
<h6>Top sản phẩm bán chạy</h6>
<div class="peity-chart-dashboard text-center chart-vertical-center">
<ngx-charts-pie-chart *ngIf="!isShop || (isShop && isData)" class="pie-colours-1"
[view]="view" [scheme]="doughnutChartColorScheme" [results]="doughnutData"
[explodeSlices]="false" [labels]="doughnutChartShowLabels" [arcWidth]=0.99
[doughnut]="true" [animations]="false" [tooltipDisabled]="doughnutChartTooltip"
[gradient]="doughnutChartGradient">
</ngx-charts-pie-chart>
</div>
<div class="order-graph-bottom sales-location">
<div class="media" *ngIf="products.length > 0">
<div class="order-shape-primary"></div>
<div class="media-body">
<h6 class="mb-0 me-0">{{ products[0]?.name }} <span class="pull-right">{{
products[0]?.sold }} lượt</span></h6>
</div>
</div>
<div class="media" *ngIf="products.length > 1">
<div class="order-shape-secondary"></div>
<div class="media-body">
<h6 class="mb-0 me-0">{{ products[1]?.name }} <span class="pull-right">{{
products[1]?.sold }} lượt</span></h6>
</div>
</div>
<div class="media" *ngIf="products.length > 2">
<div class="order-shape-danger"></div>
<div class="media-body">
<h6 class="mb-0 me-0">{{ products[2]?.name }} <span class="pull-right">{{
products[2]?.sold }} lượt</span></h6>
</div>
</div>
<div class="media" *ngIf="products.length > 3">
<div class="order-shape-warning"></div>
<div class="media-body">
<h6 class="mb-0 me-0">{{ products[3]?.name }} <span class="pull-right">{{
products[3]?.sold }} lượt</span></h6>
</div>
</div>
<div class="media" *ngIf="products.length > 4">
<div class="order-shape-success"></div>
<div class="media-body">
<h6 class="mb-0 me-0">{{ products[4]?.name }} <span class="pull-right">{{
products[4]?.sold }} lượt</span></h6>
</div>
</div>
</div>
</div>
</div>
<!-- <div class="col-xl-6 xl-100">-->
<!-- <div class="order-graph xl-space">-->
<!-- <h6>Revenue for last month</h6>-->
<!-- <div class="ct-4 flot-chart-container">-->
<!-- <x-chartist [type]="chart5.type" [data]="ch art5.data" [options]="chart5.options">-->
<!-- </x-chartist>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,3 @@
th, td {
text-align: center;
}
@@ -0,0 +1,383 @@
import { AfterViewInit, Component, OnInit } from '@angular/core';
import * as chartData from '../../shared/data/chart';
import { doughnutData, pieData } from '../../shared/data/chart';
import { ShopService } from 'src/app/shared/service/shop.service';
import { ProductService } from 'src/app/shared/service/product.service';
import { Product } from 'src/app/shared/tables/product';
import { Order } from 'src/app/shared/tables/order-list';
import { OrderService } from 'src/app/shared/service/order.service';
import { resolve } from 'path';
import { forkJoin } from 'rxjs';
import { UserService } from 'src/app/shared/service/user.service';
import { FirebaseService } from 'src/app/shared/service/firebase.service';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent implements OnInit, AfterViewInit {
//Log-in
isShop: boolean = false;
loggedId: number = Number(localStorage.getItem('user-id'))
loggedRole = localStorage.getItem('user-role');
shopId: number;
//Shop Properties
totalRevenue: number = 0;
totalProducts: number = 0;
totalReviewCount: number = 0;
totalAverageRating: number = 0;
//Orders
orders: Order[] = [];
//Pagination Properties
thePageNumber = 1;
thePageSize = 5;
sortBy = "orderTime";
sortDir = "desc";
theTotalElements = 0;
//District value
thanhKheValue: number;
sonTraValue: number;
camLeValue: number;
lienChieuValue: number;
haiChauValue: number;
hoaVangValue: number;
nguHanhSonValue: number;
hoangSaValue: number;
//Products
products: Product[] = [];
isData: boolean = false;
//Admin
totalUser: number = 0;
day: number = 30;
public doughnutData: Array<Object> = [];
public pieData;
constructor(
private userService: UserService,
private shopService: ShopService,
private productService: ProductService,
private orderService: OrderService,
private firebaseService: FirebaseService
) {
}
ngOnInit() {
if (this.loggedRole != 'ROLE_ADMIN') {
this.isShop = true;
// this.shopId = Number(localStorage.getItem('shop-id'))
const shopIdPromise = new Promise<number>((resolve) => {
const shopId = Number(localStorage.getItem('shop-id'));
resolve(shopId);
})
shopIdPromise.then((shopId) => {
this.shopId = shopId;
this.productService.getProductsByShopIdNoPageable(this.shopId).subscribe((products) => {
let i = 0;
products.forEach(product => {
this.totalReviewCount = this.totalReviewCount + product.reviewCount;
this.totalProducts = this.totalProducts + product.sold;
if (product.averageRating != 0) {
this.totalAverageRating = this.totalAverageRating + product.averageRating;
i++;
}
});
if (i != 0) {
this.totalAverageRating = this.totalAverageRating / i;
}
})
this.productService.getProductsByShopIdAndSort(this.shopId, 0, 5, 'sold', 'desc').subscribe((data) => {
this.products = data.products;
if (this.products.length > 4) {
this.isData = true;
}
this.doughtnutData(data.products);
})
this.orderService.getOrdersByShopId(this.shopId, this.thePageNumber - 1, this.thePageSize, this.sortBy, this.sortDir)
.subscribe(this.processResult());
this.shopService.getShopRevenue(this.shopId, 30).subscribe((res) => {
this.totalRevenue = res;
});
this.valueOfShopDistrict();
})
}
else {
//ADMIN DO HERE
this.userService.countNewUser(this.day).subscribe((res) => {
this.totalUser = res;
})
this.productService.getProductsNoPagination().subscribe((products) => {
let i = 0;
products.forEach(product => {
this.totalReviewCount = this.totalReviewCount + product.reviewCount;
this.totalProducts = this.totalProducts + product.sold;
if (product.averageRating != 0) {
this.totalAverageRating = this.totalAverageRating + product.averageRating;
i++;
}
});
this.totalAverageRating = this.totalAverageRating / i;
})
this.productService.getProductsPaginationAndSort(0, 5, 'sold', 'desc').subscribe((data) => {
this.products = data.products;
this.doughtnutData(data.products)
})
this.orderService
.getOrdersPagination(this.thePageNumber - 1, this.thePageSize, this.sortBy, this.sortDir)
.subscribe(this.processResult());
this.valueOfAdminDistrict();
}
}
processResult() {
return (data: any) => {
this.orders = data.orders;
this.thePageNumber = data.page.pageNo + 1;
this.thePageSize = data.page.pageSize;
this.theTotalElements = data.page.totalElements;
};
}
// doughnut 2
public view = chartData.view;
public doughnutChartColorScheme = chartData.doughnutChartcolorScheme;
public doughnutChartShowLabels = chartData.doughnutChartShowLabels;
public doughnutChartGradient = chartData.doughnutChartGradient;
public doughnutChartTooltip = chartData.doughnutChartTooltip;
public chart5 = chartData.chart5;
// lineChart
public lineChartData = chartData.lineChartData;
public lineChartLabels = chartData.lineChartLabels;
public lineChartOptions = chartData.lineChartOptions;
public lineChartColors = chartData.lineChartColors;
public lineChartLegend = chartData.lineChartLegend;
public lineChartType = chartData.lineChartType;
// lineChart
public smallLineChartData = chartData.smallLineChartData;
public smallLineChartLabels = chartData.smallLineChartLabels;
public smallLineChartOptions = chartData.smallLineChartOptions;
public smallLineChartLegend = chartData.smallLineChartLegend;
public smallLineChartType = chartData.smallLineChartType;
// lineChart
public smallLine2ChartData = chartData.smallLine2ChartData;
public smallLine2ChartLabels = chartData.smallLine2ChartLabels;
public smallLine2ChartOptions = chartData.smallLine2ChartOptions;
public smallLine2ChartLegend = chartData.smallLine2ChartLegend;
public smallLine2ChartType = chartData.smallLine2ChartType;
// lineChart
public smallLine3ChartData = chartData.smallLine3ChartData;
public smallLine3ChartLabels = chartData.smallLine3ChartLabels;
public smallLine3ChartOptions = chartData.smallLine3ChartOptions;
public smallLine3ChartLegend = chartData.smallLine3ChartLegend;
public smallLine3ChartType = chartData.smallLine3ChartType;
// lineChart
public smallLine4ChartData = chartData.smallLine4ChartData;
public smallLine4ChartLabels = chartData.smallLine4ChartLabels;
public smallLine4ChartOptions = chartData.smallLine4ChartOptions;
public smallLine4ChartColors = chartData.smallLine4ChartColors;
public smallLine4ChartLegend = chartData.smallLine4ChartLegend;
public smallLine4ChartType = chartData.smallLine4ChartType;
public chart3 = chartData.chart3;
// events
public chartClicked(e: any): void {
}
public chartHovered(e: any): void {
}
ngAfterViewInit(): void {
}
valueOfAdminDistrict() {
const requests = [
this.orderService.countOrderByDistrict('Thanh Khê'),
this.orderService.countOrderByDistrict('Sơn Trà'),
this.orderService.countOrderByDistrict('Cẩm Lệ'),
this.orderService.countOrderByDistrict('Liên Chiểu'),
this.orderService.countOrderByDistrict('Hải Châu'),
this.orderService.countOrderByDistrict('Hoà Vang'),
this.orderService.countOrderByDistrict('Ngũ Hành Sơn'),
this.orderService.countOrderByDistrict('Hoàng Sa')
];
forkJoin(requests).subscribe(
(results: number[]) => {
this.thanhKheValue = results[0];
this.sonTraValue = results[1];
this.camLeValue = results[2];
this.lienChieuValue = results[3];
this.haiChauValue = results[4];
this.hoaVangValue = results[5];
this.nguHanhSonValue = results[6];
this.hoangSaValue = results[7];
this.pieData = [
{
value: this.thanhKheValue,
name: "Quận Thanh Khê"
},
{
value: this.sonTraValue,
name: "Quận Sơn Trà"
},
{
value: this.camLeValue,
name: "Quận Cẩm Lệ"
},
{
value: this.lienChieuValue,
name: "Quận Liên Chiểu"
},
{
value: this.haiChauValue,
name: "Quận Hải Châu"
},
{
value: this.hoaVangValue,
name: "Huyện Hoà Vang"
},
{
value: this.nguHanhSonValue,
name: "Quận Ngũ Hành Sơn"
},
{
value: this.hoangSaValue,
name: "Huyện Hoàng Sa"
}
]
},
(error) => {
console.error('Error:', error);
}
);
}
valueOfShopDistrict() {
const requests = [
this.orderService.countShopOrderByDistrict(this.shopId, 'Thanh Khê'),
this.orderService.countShopOrderByDistrict(this.shopId, 'Sơn Trà'),
this.orderService.countShopOrderByDistrict(this.shopId, 'Cẩm Lệ'),
this.orderService.countShopOrderByDistrict(this.shopId, 'Liên Chiểu'),
this.orderService.countShopOrderByDistrict(this.shopId, 'Hải Châu'),
this.orderService.countShopOrderByDistrict(this.shopId, 'Hoà Vang'),
this.orderService.countShopOrderByDistrict(this.shopId, 'Ngũ Hành Sơn'),
this.orderService.countShopOrderByDistrict(this.shopId, 'Hoàng Sa')
];
forkJoin(requests).subscribe(
(results: number[]) => {
this.thanhKheValue = results[0];
this.sonTraValue = results[1];
this.camLeValue = results[2];
this.lienChieuValue = results[3];
this.haiChauValue = results[4];
this.hoaVangValue = results[5];
this.nguHanhSonValue = results[6];
this.hoangSaValue = results[7];
this.pieData = [
{
value: this.thanhKheValue,
name: "Quận Thanh Khê"
},
{
value: this.sonTraValue,
name: "Quận Sơn Trà"
},
{
value: this.camLeValue,
name: "Quận Cẩm Lệ"
},
{
value: this.lienChieuValue,
name: "Quận Liên Chiểu"
},
{
value: this.haiChauValue,
name: "Quận Hải Châu"
},
{
value: this.hoaVangValue,
name: "Huyện Hoà Vang"
},
{
value: this.nguHanhSonValue,
name: "Quận Ngũ Hành Sơn"
},
{
value: this.hoangSaValue,
name: "Huyện Hoàng Sa"
}
]
},
(error) => {
console.error('Error:', error);
}
);
}
doughtnutData(products: Product[]) {
// products.forEach((product) => {
// this.doughnutData.push({ value: product.sold, name: product.name });
// })
this.doughnutData = [
{
value: products[0]?.sold,
name: products[0]?.name
},
{
value: products[1]?.sold,
name: products[1]?.name
},
{
value: products[2]?.sold,
name: products[2]?.name
},
{
value: products[3]?.sold,
name: products[3]?.name
},
{
value: products[4]?.sold,
name: products[4]?.name
}
];
}
}
@@ -0,0 +1,26 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DashboardRoutingModule } from './dashboard-routing.module';
import { DashboardComponent } from './dashboard.component';
import { CountToModule } from 'angular-count-to';
import { NgChartsModule } from 'ng2-charts';
import { Ng2GoogleChartsModule } from 'ng2-google-charts';
import { NgxChartsModule } from '@swimlane/ngx-charts';
import { ChartistModule } from 'ng-chartist';
import { SharedModule } from '../../shared/shared.module';
@NgModule({
declarations: [DashboardComponent],
imports: [
CommonModule,
DashboardRoutingModule,
CountToModule,
SharedModule,
NgChartsModule,
Ng2GoogleChartsModule,
NgxChartsModule,
ChartistModule
]
})
export class DashboardModule { }
@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { InvoiceComponent } from './invoice.component';
const routes: Routes = [
{
path: '',
component: InvoiceComponent,
data: {
title: "Invoice",
breadcrumb: "Invoice"
}
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class InvoiceRoutingModule { }
@@ -0,0 +1,57 @@
<!-- Container-fluid starts-->
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-header">
<h5>Invoice List</h5>
</div>
<div class="card-body">
<div id="batchDelete" class="custom-datatable">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th scope="col" sortable="no">No</th>
<th scope="col" sortable="invoice">Invoice</th>
<th scope="col" sortable="name" >Date</th>
<th scope="col" sortable="name" >Shipping</th>
<th scope="col" sortable="name" >Amount</th>
<th scope="col" sortable="name" >Tax</th>
<th scope="col" sortable="name" >Total</th>
<th scope="col" sortable="name" >Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of tableItem$ | async ">
<td scope="row">
<span>{{item.no}}</span>
</td>
<td>
{{item.invoice}}
</td>
<td>{{item.date}}</td>
<td>{{item.shipping}}</td>
<td>{{item.amount}}</td>
<td>{{item.tax}}</td>
<td>{{item.total}}</td>
<td>
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>&nbsp;
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
</td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-center p-2">
<ngb-pagination
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
</ngb-pagination>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,45 @@
import { DecimalPipe } from '@angular/common';
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
import { Observable } from 'rxjs';
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
import { TableService } from 'src/app/shared/service/table.service';
import { InvoiceDB, INVOICEDB } from '../../shared/tables/invoice';
@Component({
selector: 'app-invoice',
templateUrl: './invoice.component.html',
styleUrls: ['./invoice.component.scss'],
providers: [TableService, DecimalPipe]
})
export class InvoiceComponent implements OnInit {
public tableItem$: Observable<InvoiceDB[]>;
public searchText;
total$: Observable<number>;
constructor(public service: TableService) {
this.tableItem$ = service.tableItem$;
this.total$ = service.total$;
this.service.setUserData(INVOICEDB)
}
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
onSort({ column, direction }: SortEvent) {
// resetting other headers
this.headers.forEach((header) => {
if (header.sortable !== column) {
header.direction = '';
}
});
this.service.sortColumn = column;
this.service.sortDirection = direction;
}
ngOnInit() {
}
}
@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { InvoiceRoutingModule } from './invoice-routing.module';
import { InvoiceComponent } from './invoice.component';
import { SharedModule } from 'src/app/shared/shared.module';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({
declarations: [InvoiceComponent],
imports: [
CommonModule,
InvoiceRoutingModule,
SharedModule,
NgbModule
]
})
export class InvoiceModule { }
@@ -0,0 +1,39 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { RatesComponent } from './rates/rates.component';
import { TranslationsComponent } from './translations/translations.component';
import { TaxesComponent } from './taxes/taxes.component';
const routes: Routes = [
{
path: 'translations',
component: TranslationsComponent,
data: {
title: "Translations",
breadcrumb: "Translations"
}
},
{
path: 'currency-rates',
component: RatesComponent,
data: {
title: "Currency Rates",
breadcrumb: "Currency Rates"
}
},
{
path: 'taxes',
component: TaxesComponent,
data: {
title: 'Taxes',
breadcrumb: 'Taxes'
}
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LocalizationRoutingModule { }
@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LocalizationRoutingModule } from './localization-routing.module';
import { TranslationsComponent } from './translations/translations.component';
import { RatesComponent } from './rates/rates.component';
import { TaxesComponent } from './taxes/taxes.component';
import { SharedModule } from 'src/app/shared/shared.module';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({
declarations: [TranslationsComponent, RatesComponent, TaxesComponent],
imports: [
CommonModule,
LocalizationRoutingModule,
SharedModule,
NgbModule
]
})
export class LocalizationModule { }
@@ -0,0 +1,46 @@
<!-- Container-fluid starts-->
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-header">
<h5>Currency Details</h5>
</div>
<div class="card-body">
<div id="batchDelete" class="custom-datatable">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th scope="col" sortable="title" (sort)="onSort($event)">Currency Title</th>
<th scope="col" sortable="usd" (sort)="onSort($event)">USD</th>
<th scope="col" sortable="code" (sort)="onSort($event)">Code</th>
<th scope="col" sortable="rate" (sort)="onSort($event)">Exchange Rate</th>
<th scope="col" >Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of tableItem$ | async ">
<td scope="row">
<span>{{item.title}}</span>
</td>
<td>
{{item.usd}}
</td>
<td>{{item.code}}</td>
<td>{{item.rate}}</td>
<td>
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>&nbsp;
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,46 @@
import { DecimalPipe } from '@angular/common';
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
import { Observable } from 'rxjs';
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
import { TableService } from 'src/app/shared/service/table.service';
import { RateDB, RATEDB } from '../../../shared/tables/rate';
@Component({
selector: 'app-rates',
templateUrl: './rates.component.html',
styleUrls: ['./rates.component.scss'],
providers: [TableService, DecimalPipe]
})
export class RatesComponent implements OnInit {
public tableItem$: Observable<RateDB[]>;
public searchText;
total$: Observable<number>;
constructor(public service: TableService) {
this.tableItem$ = service.tableItem$;
this.total$ = service.total$;
this.service.setUserData(RATEDB)
}
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
onSort({ column, direction }: SortEvent) {
// resetting other headers
this.headers.forEach((header) => {
if (header.sortable !== column) {
header.direction = '';
}
});
this.service.sortColumn = column;
this.service.sortDirection = direction;
}
ngOnInit() {
}
}
@@ -0,0 +1,46 @@
<!-- Container-fluid starts-->
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-header">
<h5>Tax Details</h5>
</div>
<div class="card-body">
<div id="batchDelete" class="custom-datatable">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th scope="col" sortable="detail" (sort)="onSort($event)">Currency Title</th>
<th scope="col" sortable="tax_schedule" (sort)="onSort($event)">USD</th>
<th scope="col" sortable="rate" (sort)="onSort($event)" >Code</th>
<th scope="col" sortable="total_amount" (sort)="onSort($event)">Exchange Rate</th>
<th scope="col" (sort)="onSort($event)">Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of tableItem$ | async ">
<td scope="row">
<span>{{item.detail}}</span>
</td>
<td>
{{item.tax_schedule}}
</td>
<td>{{item.rate}}</td>
<td>{{item.total_amount}}</td>
<td>
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>&nbsp;
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,43 @@
import { DecimalPipe } from '@angular/common';
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
import { Observable } from 'rxjs';
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
import { TableService } from 'src/app/shared/service/table.service';
import { TaxesDB, TAXESDB } from '../../../shared/tables/taxes';
@Component({
selector: 'app-taxes',
templateUrl: './taxes.component.html',
styleUrls: ['./taxes.component.scss'],
providers: [TableService, DecimalPipe]
})
export class TaxesComponent implements OnInit {
public tableItem$: Observable<TaxesDB[]>;
public searchText;
total$: Observable<number>;
constructor(public service: TableService) {
this.tableItem$ = service.tableItem$;
this.total$ = service.total$;
this.service.setUserData(TAXESDB)
}
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
onSort({ column, direction }: SortEvent) {
// resetting other headers
this.headers.forEach((header) => {
if (header.sortable !== column) {
header.direction = '';
}
});
this.service.sortColumn = column;
this.service.sortDirection = direction;
}
ngOnInit() {
}
}
@@ -0,0 +1,51 @@
<!-- Container-fluid starts-->
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-header">
<h5>Translations</h5>
</div>
<div class="card-body">
<div id="batchDelete" class="custom-datatable">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th scope="col" sortable="user_key" (sort)="onSort($event)">User Key</th>
<th scope="col" sortable="russian" (sort)="onSort($event)">Russian</th>
<th scope="col" sortable="arabic" (sort)="onSort($event)">Arbic</th>
<th scope="col" sortable="english" (sort)="onSort($event)">English</th>
<th scope="col" >Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of tableItem$ | async ">
<td scope="row">
<span>{{item.user_key}}</span>
</td>
<td>
{{item.russian}}
</td>
<td>{{item.arabic}}</td>
<td>{{item.english}}</td>
<td>
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>&nbsp;
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
</td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-center p-2">
<ngb-pagination
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
</ngb-pagination>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,45 @@
import { DecimalPipe } from '@angular/common';
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
import { Observable } from 'rxjs';
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
import { TableService } from 'src/app/shared/service/table.service';
import { TRANSLATIONDB, TranslationsDB } from '../../../shared/tables/translations';
@Component({
selector: 'app-translations',
templateUrl: './translations.component.html',
styleUrls: ['./translations.component.scss'],
providers: [TableService, DecimalPipe]
})
export class TranslationsComponent implements OnInit {
public tableItem$: Observable<TranslationsDB[]>;
public searchText;
total$: Observable<number>;
constructor(public service: TableService) {
this.tableItem$ = service.tableItem$;
this.total$ = service.total$;
this.service.setUserData(TRANSLATIONDB)
}
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
onSort({ column, direction }: SortEvent) {
// resetting other headers
this.headers.forEach((header) => {
if (header.sortable !== column) {
header.direction = '';
}
});
this.service.sortColumn = column;
this.service.sortDirection = direction;
}
ngOnInit() {
}
}
@@ -0,0 +1,25 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { MediaComponent } from './media/media.component';
const routes: Routes = [
{
path: '',
children: [
{
path: '',
component: MediaComponent,
data: {
title: "Media",
breadcrumb: "Media"
}
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class MediaRoutingModule { }
+21
View File
@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MediaRoutingModule } from './media-routing.module';
import { MediaComponent } from './media/media.component';
import { NgxDropzoneModule } from 'ngx-dropzone';
import { SharedModule } from 'src/app/shared/shared.module';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({
declarations: [MediaComponent],
imports: [
CommonModule,
MediaRoutingModule,
NgxDropzoneModule,
SharedModule,
NgbModule
]
})
export class MediaModule { }
@@ -0,0 +1,67 @@
<!-- Container-fluid starts-->
<div class="container-fluid bulk-cate">
<div class="card ">
<div class="card-header">
<h5>Dropzone Media</h5>
</div>
<div class="card-body">
<div class="dropzone-custom">
<ngx-dropzone class="dropzone-border" (change)="onSelect($event)">
<ngx-dropzone-label>
<div class="dz-message needsclick">
<i class="fa fa-cloud-upload"></i>
<h4 class="mb-0 f-w-600">Drop files here or click to upload.</h4>
</div>
</ngx-dropzone-label>
<ngx-dropzone-preview *ngFor="let f of files" [removable]="true" (removed)="onRemove(f)">
<ngx-dropzone-label>{{ f.name }} ({{ f.type }})</ngx-dropzone-label>
</ngx-dropzone-preview>
</ngx-dropzone>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h5>Media File List</h5>
</div>
<div class="card-body">
<div id="batchDelete" class="category-table custom-datatable media-datatable">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th scope="col" sortable="action">Actions</th>
<th scope="col" sortable="img" (sort)="onSort($event)">Image</th>
<th scope="col" sortable="file_name" (sort)="onSort($event)">File Name</th>
<th scope="col" sortable="url" (sort)="onSort($event)">URL</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of tableItem$ | async ">
<td>
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>&nbsp;
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
</td>
<td scope="row">
<img [src]="item.img" class="imgTable" style="width: 20px">
</td>
<td>
{{item.file_name}}
</td>
<td>{{item.url}}</td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-center p-2 border-top-0">
<ngb-pagination
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
</ngb-pagination>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,55 @@
import { DecimalPipe } from '@angular/common';
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
import { DropzoneConfigInterface } from 'ngx-dropzone-wrapper';
import { Observable } from 'rxjs';
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
import { TableService } from 'src/app/shared/service/table.service';
import { MediaDB, MEDIADB } from 'src/app/shared/tables/media';
@Component({
selector: 'app-media',
templateUrl: './media.component.html',
styleUrls: ['./media.component.scss'],
providers: [TableService, DecimalPipe]
})
export class MediaComponent implements OnInit {
public tableItem$: Observable<MediaDB[]>;
public searchText;
total$: Observable<number>;
constructor(public service: TableService) {
this.tableItem$ = service.tableItem$;
this.total$ = service.total$;
this.service.setUserData(MEDIADB)
}
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
onSort({ column, direction }: SortEvent) {
// resetting other headers
this.headers.forEach((header) => {
if (header.sortable !== column) {
header.direction = '';
}
});
this.service.sortColumn = column;
this.service.sortDirection = direction;
}
files: File[] = [];
onSelect(event) {
this.files.push(...event.addedFiles);
}
onRemove(event) {
this.files.splice(this.files.indexOf(event), 1);
}
ngOnInit() {
}
}
@@ -0,0 +1,31 @@
<!-- Container-fluid starts-->
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-header">
<h5>Add Menu</h5>
</div>
<div class="card-body">
<form class="needs-validation">
<div class="form-group row">
<div class="col-xl-3 col-md-4"><label for="validationCustom0"><span>*</span> Menu Name</label></div>
<div class="col-md-8"><input class="form-control" id="validationCustom0" type="text" required=""></div>
</div>
<div clalass="form-group row">
<label class="col-xl-3 col-md-4">Status</label>
<div class="col-xl-9 col-md-8">
<div class="checkbox checkbox-primary">
<input id="checkbox-primary-2" type="checkbox" data-original-title="" title="">
<label for="checkbox-primary-2">Enable the Menu</label>
</div>
</div>
</div>
<button type="button" class="btn btn-primary">Save</button>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-create-menu',
templateUrl: './create-menu.component.html',
styleUrls: ['./create-menu.component.scss']
})
export class CreateMenuComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
@@ -0,0 +1,45 @@
<!-- Container-fluid starts-->
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-header">
<h5>Menu Lists</h5>
</div>
<div class="card-body">
<div class="custom-datatable">
<button class="btn btn-primary me-1 mb-3" style="float: right;" >Delete</button>
<div class="clearfix"></div>
<table class="table table-striped">
<thead>
<tr>
<th></th>
<th scope="col" sortable="name" (sort)="onSort($event)">Name</th>
<th scope="col" sortable="status" (sort)="onSort($event)">Status</th>
<th scope="col" sortable="created_on" (sort)="onSort($event)">Created On</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of tableItem$ | async ">
<th> <input type="checkbox" name="title_select" onclick="onSelect(item.id)"></th>
<td scope="row">
{{item.name}}
</td>
<td>
<i class="fa fa-circle {{item.status}} f-12"></i>
</td>
<td>{{item.created_on}}</td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-center p-2">
<ngb-pagination
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
</ngb-pagination>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,51 @@
import { DecimalPipe } from '@angular/common';
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
import { Observable } from 'rxjs';
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
import { TableService } from 'src/app/shared/service/table.service';
import { ListPagesDB } from 'src/app/shared/tables/list-pages';
import { MENUDB, MenuDB } from 'src/app/shared/tables/menu';
@Component({
selector: 'app-list-menu',
templateUrl: './list-menu.component.html',
styleUrls: ['./list-menu.component.scss'],
providers: [TableService, DecimalPipe]
})
export class ListMenuComponent implements OnInit {
public selected = [];
public tableItem$: Observable<ListPagesDB[]>;
public searchText;
total$: Observable<number>;
constructor(public service: TableService) {
this.tableItem$ = service.tableItem$;
this.total$ = service.total$;
this.service.setUserData(MENUDB)
}
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
onSort({ column, direction }: SortEvent) {
// resetting other headers
this.headers.forEach((header) => {
if (header.sortable !== column) {
header.direction = '';
}
});
this.service.sortColumn = column;
this.service.sortDirection = direction;
}
onSelect({ selected }) {
this.selected.splice(0, this.selected.length);
this.selected.push(...selected);
}
ngOnInit() { }
}
@@ -0,0 +1,34 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ListMenuComponent } from './list-menu/list-menu.component';
import { CreateMenuComponent } from './create-menu/create-menu.component';
const routes: Routes = [
{
path: '',
children: [
{
path: 'list-menu',
component: ListMenuComponent,
data: {
title: "Menu Lists",
breadcrumb: "Menu Lists"
}
},
{
path: 'create-menu',
component: CreateMenuComponent,
data: {
title: "Create Menu",
breadcrumb: "Create Menu"
}
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class MenusRoutingModule { }
+21
View File
@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
// import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { MenusRoutingModule } from './menus-routing.module';
import { ListMenuComponent } from './list-menu/list-menu.component';
import { CreateMenuComponent } from './create-menu/create-menu.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { SharedModule } from 'src/app/shared/shared.module';
@NgModule({
declarations: [ListMenuComponent, CreateMenuComponent],
imports: [
CommonModule,
MenusRoutingModule,
NgbModule,
SharedModule
]
})
export class MenusModule { }
@@ -0,0 +1,79 @@
<!-- Container-fluid starts-->
<div class="container-fluid">
<div class="card">
<div class="card-header">
<h5>Add Page</h5>
</div>
<div class="card-body tab2-card">
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
<li [ngbNavItem]="1">
<a ngbNavLink>General</a>
<ng-template ngbNavContent>
<form [formGroup]="generalForm" class="needs-validation" novalidate="">
<h4>General</h4>
<div class="form-group row align-items-center">
<div class="col-xl-3 col-md-4"><label for="validationCustom0"><span>*</span> Name</label></div>
<div class="col-xl-8 col-md-7"><input class="form-control" formControlName="name"
id="validationCustom0" type="text" required="">
</div>
</div>
<div class="form-group row editor-label align-items-center">
<div class="col-xl-3 col-md-4"><label>Description</label></div>
<div class="col-xl-8 col-md-7"><textarea class="w-100" rows="4" formControlName="desc"></textarea></div>
</div>
<div class="form-group row">
<div class="col-xl-3 col-md-4"><label>Status</label></div>
<div class="col-xl-8 col-md-7"><div class="checkbox checkbox-primary">
<input id="checkbox-primary-2" formControlName="status" type="checkbox"
data-original-title="" title="">
<label for="checkbox-primary-2">Enable the Page</label>
</div>
</div>
</div>
</form>
</ng-template>
</li>
<li [ngbNavItem]="2">
<a ngbNavLink>SEO</a>
<ng-template ngbNavContent>
<form [formGroup]="seoForm" class="needs-validation" novalidate="">
<h4>SEO</h4>
<div class="form-group row">
<div class="col-xl-3 col-md-4"><label for="validationCustom2">Meta Title</label></div>
<div class="col-xl-8 col-md-7"><input class="form-control" formControlName="title"
id="validationCustom2" type="text" required=""></div>
</div>
<div class="form-group row align-items-center editor-label">
<div class="col-xl-3 col-md-4"><label>Meta Description</label></div>
<div class="col-xl-8 col-md-7"><textarea rows="4" class="w-100" formControlName="meta_desc"></textarea></div>
</div>
</form>
</ng-template>
</li>
<!-- <li [ngbNavItem]="3">
<a ngbNavLink>Usage</a>
<ng-template ngbNavContent>
<form [formGroup]="usageForm" class="needs-validation" novalidate="">
<h4>Usage Limits</h4>
<div class="form-group row">
<label for="validationCustom6" class="col-xl-3 col-md-4">Per Limit</label>
<input class="form-control col-md-7" formControlName="limit" id="validationCustom6"
type="number">
</div>
<div class="form-group row">
<label for="validationCustom7" class="col-xl-3 col-md-4">Per Customer</label>
<input class="form-control col-md-7" formControlName="customer" id="validationCustom7"
type="number">
</div>
</form>
</ng-template>
</li> -->
</ul>
<div [ngbNavOutlet]="nav" class="mt-2"></div>
<div class="pull-right">
<button type="button" class="btn btn-primary">Save</button>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,37 @@
import { Component, OnInit } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
@Component({
selector: 'app-create-page',
templateUrl: './create-page.component.html',
styleUrls: ['./create-page.component.scss']
})
export class CreatePageComponent implements OnInit {
public generalForm: UntypedFormGroup;
public seoForm: UntypedFormGroup;
public active = 1;
constructor(private formBuilder: UntypedFormBuilder) {
this.createGeneralForm();
this.createSeoForm();
}
createGeneralForm() {
this.generalForm = this.formBuilder.group({
name: [''],
desc: [''],
status: ['']
})
}
createSeoForm() {
this.seoForm = this.formBuilder.group({
title: [''],
keyword: [''],
meta_desc: ['']
})
}
ngOnInit() {
}
}
@@ -0,0 +1,43 @@
<!-- Container-fluid starts-->
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-header">
<h5>Page Details</h5>
</div>
<div class="card-body">
<div class="custom-datatable">
<button class="btn btn-primary me-1 mb-3" style="float: right;">Delete</button>
<div class="clearfix"></div>
<table class="table table-striped">
<thead>
<tr>
<th> </th>
<th scope="col" sortable="name" (sort)="onSort($event)">Name</th>
<th scope="col" sortable="status" (sort)="onSort($event)">Status</th>
<th scope="col" sortable="created_on" (sort)="onSort($event)">Created On</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of tableItem$ | async ">
<th> <input type="checkbox" name="title_select" onclick="onSelect(item.id)"></th>
<th scope="row">
{{ item.name }}</th>
<td><i class="fa fa-circle {{item.status}} f-12"></i></td>
<td>{{item.created_on}}</td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-center p-2">
<ngb-pagination
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
</ngb-pagination>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Container-fluid Ends-->
@@ -0,0 +1,49 @@
import { DecimalPipe } from '@angular/common';
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
import { Observable } from 'rxjs';
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
import { TableService } from 'src/app/shared/service/table.service';
import { LISTPAGEDB, ListPagesDB } from 'src/app/shared/tables/list-pages';
@Component({
selector: 'app-list-page',
templateUrl: './list-page.component.html',
styleUrls: ['./list-page.component.scss'],
providers: [TableService, DecimalPipe]
})
export class ListPageComponent implements OnInit {
public selected = [];
public tableItem$: Observable<ListPagesDB[]>;
public searchText;
total$: Observable<number>;
constructor(public service: TableService) {
this.tableItem$ = service.tableItem$;
this.total$ = service.total$;
this.service.setUserData(LISTPAGEDB)
}
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
onSort({ column, direction }: SortEvent) {
// resetting other headers
this.headers.forEach((header) => {
if (header.sortable !== column) {
header.direction = '';
}
});
this.service.sortColumn = column;
this.service.sortDirection = direction;
}
onSelect({ selected }) {
this.selected.splice(0, this.selected.length);
this.selected.push(...selected);
}
ngOnInit() {
}
}
@@ -0,0 +1,34 @@
import { NgModule, Component } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ListPageComponent } from './list-page/list-page.component';
import { CreatePageComponent } from './create-page/create-page.component';
const routes: Routes = [
{
path: '',
children: [
{
path: 'list-page',
component: ListPageComponent,
data: {
title: "List Page",
breadcrumb: "List Page"
}
},
{
path: 'create-page',
component: CreatePageComponent,
data: {
title: "Create Page",
breadcrumb: "Create Page"
}
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class PagesRoutingModule { }
+21
View File
@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { ReactiveFormsModule } from '@angular/forms';
import { PagesRoutingModule } from './pages-routing.module';
import { ListPageComponent } from './list-page/list-page.component';
import { CreatePageComponent } from './create-page/create-page.component';
import { SharedModule } from 'src/app/shared/shared.module';
@NgModule({
declarations: [ListPageComponent, CreatePageComponent],
imports: [
CommonModule,
PagesRoutingModule,
NgbModule,
ReactiveFormsModule,
SharedModule
]
})
export class PagesModule { }
@@ -0,0 +1,108 @@
<div class="container-fluid">
<div class="row product-adding">
<div class="col-xl-6">
<div class="card">
<div class="card-header">
<h5>General</h5>
</div>
<div class="card-body">
<div class="digital-add needs-validation">
<div class="form-group">
<label for="validationCustom01" class="col-form-label pt-0"><span>*</span> Title</label>
<input class="form-control" id="validationCustom01" type="text" required="">
</div>
<div class="form-group">
<label for="validationCustomtitle" class="col-form-label pt-0"><span>*</span> SKU</label>
<input class="form-control" id="validationCustomtitle" type="text" required="">
</div>
<div class="form-group">
<label class="col-form-label"><span>*</span> Categories</label>
<select class="custom-select form-select" required="">
<option value="">--Select--</option>
<option value="1">eBooks</option>
<option value="2">Graphic Design</option>
<option value="3">3D Impact</option>
<option value="4">Application</option>
<option value="5">Websites</option>
</select>
</div>
<div class="form-group">
<label class="col-form-label">Sort Summary</label>
<textarea rows="5" cols="12"></textarea>
</div>
<div class="form-group">
<label for="validationCustom02" class="col-form-label"><span>*</span> Product Price</label>
<input class="form-control" id="validationCustom02" type="text" required="">
</div>
<div class="form-group">
<label class="col-form-label"><span>*</span> Status</label>
<div class="m-checkbox-inline mb-0 custom-radio-ml d-flex radio-animated">
<label class="d-block" for="edo-ani">
<input class="radio_animated" id="edo-ani" type="radio" name="rdo-ani">
Enable
</label>
<label class="d-block" for="edo-ani1">
<input class="radio_animated" id="edo-ani1" type="radio" name="rdo-ani">
Disable
</label>
</div>
</div>
<label class="col-form-label pt-0"> Product Upload</label>
<div class="card-body dropzone-custom p-0">
<ngx-dropzone class="dropzone-border" (change)="onSelect($event)">
<ngx-dropzone-label>
<div class="dz-message needsclick">
<i class="fa fa-cloud-upload"></i>
<h4 class="mb-0 f-w-600">Drop files here or click to upload.</h4>
</div>
</ngx-dropzone-label>
<ngx-dropzone-preview *ngFor="let f of files" [removable]="true" (removed)="onRemove(f)">
<ngx-dropzone-label>{{ f.name }} ({{ f.type }})</ngx-dropzone-label>
</ngx-dropzone-preview>
</ngx-dropzone>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-6">
<div class="card h-auto">
<div class="card-header">
<h5>Add Description</h5>
</div>
<div class="card-body">
<div class="digital-add needs-validation">
<div class="form-group mb-0">
<div class="description-sm">
<ckeditor [editor]="Editor" data="<p>Hello, world!</p>"></ckeditor>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h5>Meta Data</h5>
</div>
<div class="card-body">
<div class="digital-add needs-validation">
<div class="form-group">
<label for="validationCustom05" class="col-form-label pt-0"> Meta Title</label>
<input class="form-control" id="validationCustom05" type="text" required="">
</div>
<div class="form-group">
<label class="col-form-label">Meta Description</label>
<textarea rows="4" cols="12"></textarea>
</div>
<div class="form-group mb-0">
<div class="product-buttons text-end mt-3">
<button type="button" class="btn btn-primary">Add</button>
<button type="button" class="btn btn-light">Discard</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More