added login
This commit is contained in:
@@ -0,0 +1,366 @@
|
|||||||
|
AWSTemplateFormatVersion: 2010-09-09
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
env:
|
||||||
|
Type: String
|
||||||
|
authRoleArn:
|
||||||
|
Type: String
|
||||||
|
unauthRoleArn:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
identityPoolName:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
allowUnauthenticatedIdentities:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
resourceNameTruncated:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
userPoolName:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
autoVerifiedAttributes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
mfaConfiguration:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
mfaTypes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
smsAuthenticationMessage:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
smsVerificationMessage:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
emailVerificationSubject:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
emailVerificationMessage:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
defaultPasswordPolicy:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
passwordPolicyMinLength:
|
||||||
|
Type: Number
|
||||||
|
|
||||||
|
passwordPolicyCharacters:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
requiredAttributes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
userpoolClientGenerateSecret:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
userpoolClientRefreshTokenValidity:
|
||||||
|
Type: Number
|
||||||
|
|
||||||
|
userpoolClientWriteAttributes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
userpoolClientReadAttributes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
userpoolClientLambdaRole:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
userpoolClientSetAttributes:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
resourceName:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
authSelections:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
useDefault:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
usernameAttributes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
dependsOn:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
Conditions:
|
||||||
|
ShouldNotCreateEnvResources: !Equals [ !Ref env, NONE ]
|
||||||
|
|
||||||
|
Resources:
|
||||||
|
|
||||||
|
|
||||||
|
# BEGIN SNS ROLE RESOURCE
|
||||||
|
SNSRole:
|
||||||
|
# Created to allow the UserPool SMS Config to publish via the Simple Notification Service during MFA Process
|
||||||
|
Type: AWS::IAM::Role
|
||||||
|
Properties:
|
||||||
|
RoleName: !If [ShouldNotCreateEnvResources, 'hpiotw78e5977f_sns-role', !Join ['',['hpiotw78e5977f_sns-role', '-', !Ref env]]]
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Sid: ""
|
||||||
|
Effect: "Allow"
|
||||||
|
Principal:
|
||||||
|
Service: "cognito-idp.amazonaws.com"
|
||||||
|
Action:
|
||||||
|
- "sts:AssumeRole"
|
||||||
|
Condition:
|
||||||
|
StringEquals:
|
||||||
|
sts:ExternalId: hpiotw78e5977f_role_external_id
|
||||||
|
Policies:
|
||||||
|
-
|
||||||
|
PolicyName: hpiotw78e5977f-sns-policy
|
||||||
|
PolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
-
|
||||||
|
Effect: "Allow"
|
||||||
|
Action:
|
||||||
|
- "sns:Publish"
|
||||||
|
Resource: "*"
|
||||||
|
# BEGIN USER POOL RESOURCES
|
||||||
|
UserPool:
|
||||||
|
# Created upon user selection
|
||||||
|
# Depends on SNS Role for Arn if MFA is enabled
|
||||||
|
Type: AWS::Cognito::UserPool
|
||||||
|
UpdateReplacePolicy: Retain
|
||||||
|
Properties:
|
||||||
|
UserPoolName: !If [ShouldNotCreateEnvResources, !Ref userPoolName, !Join ['',[!Ref userPoolName, '-', !Ref env]]]
|
||||||
|
|
||||||
|
Schema:
|
||||||
|
|
||||||
|
-
|
||||||
|
Name: email
|
||||||
|
Required: true
|
||||||
|
Mutable: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AutoVerifiedAttributes: !Ref autoVerifiedAttributes
|
||||||
|
|
||||||
|
|
||||||
|
EmailVerificationMessage: !Ref emailVerificationMessage
|
||||||
|
EmailVerificationSubject: !Ref emailVerificationSubject
|
||||||
|
|
||||||
|
Policies:
|
||||||
|
PasswordPolicy:
|
||||||
|
MinimumLength: !Ref passwordPolicyMinLength
|
||||||
|
RequireLowercase: false
|
||||||
|
RequireNumbers: false
|
||||||
|
RequireSymbols: false
|
||||||
|
RequireUppercase: false
|
||||||
|
|
||||||
|
UsernameAttributes: !Ref usernameAttributes
|
||||||
|
|
||||||
|
MfaConfiguration: !Ref mfaConfiguration
|
||||||
|
SmsVerificationMessage: !Ref smsVerificationMessage
|
||||||
|
SmsConfiguration:
|
||||||
|
SnsCallerArn: !GetAtt SNSRole.Arn
|
||||||
|
ExternalId: hpiotw78e5977f_role_external_id
|
||||||
|
|
||||||
|
|
||||||
|
UserPoolClientWeb:
|
||||||
|
# Created provide application access to user pool
|
||||||
|
# Depends on UserPool for ID reference
|
||||||
|
Type: "AWS::Cognito::UserPoolClient"
|
||||||
|
Properties:
|
||||||
|
ClientName: hpiotw78e5977f_app_clientWeb
|
||||||
|
|
||||||
|
RefreshTokenValidity: !Ref userpoolClientRefreshTokenValidity
|
||||||
|
UserPoolId: !Ref UserPool
|
||||||
|
DependsOn: UserPool
|
||||||
|
UserPoolClient:
|
||||||
|
# Created provide application access to user pool
|
||||||
|
# Depends on UserPool for ID reference
|
||||||
|
Type: "AWS::Cognito::UserPoolClient"
|
||||||
|
Properties:
|
||||||
|
ClientName: hpiotw78e5977f_app_client
|
||||||
|
|
||||||
|
GenerateSecret: !Ref userpoolClientGenerateSecret
|
||||||
|
RefreshTokenValidity: !Ref userpoolClientRefreshTokenValidity
|
||||||
|
UserPoolId: !Ref UserPool
|
||||||
|
DependsOn: UserPool
|
||||||
|
# BEGIN USER POOL LAMBDA RESOURCES
|
||||||
|
UserPoolClientRole:
|
||||||
|
# Created to execute Lambda which gets userpool app client config values
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
RoleName: !If [ShouldNotCreateEnvResources, !Ref userpoolClientLambdaRole, !Join ['',[!Ref userpoolClientLambdaRole, '-', !Ref env]]]
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: '2012-10-17'
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- lambda.amazonaws.com
|
||||||
|
Action:
|
||||||
|
- 'sts:AssumeRole'
|
||||||
|
DependsOn: UserPoolClient
|
||||||
|
UserPoolClientLambda:
|
||||||
|
# Lambda which gets userpool app client config values
|
||||||
|
# Depends on UserPool for id
|
||||||
|
# Depends on UserPoolClientRole for role ARN
|
||||||
|
Type: 'AWS::Lambda::Function'
|
||||||
|
Properties:
|
||||||
|
Code:
|
||||||
|
ZipFile: !Join
|
||||||
|
- |+
|
||||||
|
- - 'const response = require(''cfn-response'');'
|
||||||
|
- 'const aws = require(''aws-sdk'');'
|
||||||
|
- 'const identity = new aws.CognitoIdentityServiceProvider();'
|
||||||
|
- 'exports.handler = (event, context, callback) => {'
|
||||||
|
- ' if (event.RequestType == ''Delete'') { '
|
||||||
|
- ' response.send(event, context, response.SUCCESS, {})'
|
||||||
|
- ' }'
|
||||||
|
- ' if (event.RequestType == ''Update'' || event.RequestType == ''Create'') {'
|
||||||
|
- ' const params = {'
|
||||||
|
- ' ClientId: event.ResourceProperties.clientId,'
|
||||||
|
- ' UserPoolId: event.ResourceProperties.userpoolId'
|
||||||
|
- ' };'
|
||||||
|
- ' identity.describeUserPoolClient(params).promise()'
|
||||||
|
- ' .then((res) => {'
|
||||||
|
- ' response.send(event, context, response.SUCCESS, {''appSecret'': res.UserPoolClient.ClientSecret});'
|
||||||
|
- ' })'
|
||||||
|
- ' .catch((err) => {'
|
||||||
|
- ' response.send(event, context, response.FAILED, {err});'
|
||||||
|
- ' });'
|
||||||
|
- ' }'
|
||||||
|
- '};'
|
||||||
|
Handler: index.handler
|
||||||
|
Runtime: nodejs8.10
|
||||||
|
Timeout: '300'
|
||||||
|
Role: !GetAtt
|
||||||
|
- UserPoolClientRole
|
||||||
|
- Arn
|
||||||
|
DependsOn: UserPoolClientRole
|
||||||
|
UserPoolClientLambdaPolicy:
|
||||||
|
# Sets userpool policy for the role that executes the Userpool Client Lambda
|
||||||
|
# Depends on UserPool for Arn
|
||||||
|
# Marked as depending on UserPoolClientRole for easier to understand CFN sequencing
|
||||||
|
Type: 'AWS::IAM::Policy'
|
||||||
|
Properties:
|
||||||
|
PolicyName: hpiotw78e5977f_userpoolclient_lambda_iam_policy
|
||||||
|
Roles:
|
||||||
|
- !If [ShouldNotCreateEnvResources, !Ref userpoolClientLambdaRole, !Join ['',[!Ref userpoolClientLambdaRole, '-', !Ref env]]]
|
||||||
|
PolicyDocument:
|
||||||
|
Version: '2012-10-17'
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 'cognito-idp:DescribeUserPoolClient'
|
||||||
|
Resource: !GetAtt UserPool.Arn
|
||||||
|
DependsOn: UserPoolClientLambda
|
||||||
|
UserPoolClientLogPolicy:
|
||||||
|
# Sets log policy for the role that executes the Userpool Client Lambda
|
||||||
|
# Depends on UserPool for Arn
|
||||||
|
# Marked as depending on UserPoolClientLambdaPolicy for easier to understand CFN sequencing
|
||||||
|
Type: 'AWS::IAM::Policy'
|
||||||
|
Properties:
|
||||||
|
PolicyName: hpiotw78e5977f_userpoolclient_lambda_log_policy
|
||||||
|
Roles:
|
||||||
|
- !If [ShouldNotCreateEnvResources, !Ref userpoolClientLambdaRole, !Join ['',[!Ref userpoolClientLambdaRole, '-', !Ref env]]]
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 'logs:CreateLogGroup'
|
||||||
|
- 'logs:CreateLogStream'
|
||||||
|
- 'logs:PutLogEvents'
|
||||||
|
Resource: !Sub
|
||||||
|
- arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*
|
||||||
|
- { region: !Ref "AWS::Region", account: !Ref "AWS::AccountId", lambda: !Ref UserPoolClientLambda}
|
||||||
|
DependsOn: UserPoolClientLambdaPolicy
|
||||||
|
UserPoolClientInputs:
|
||||||
|
# Values passed to Userpool client Lambda
|
||||||
|
# Depends on UserPool for Id
|
||||||
|
# Depends on UserPoolClient for Id
|
||||||
|
# Marked as depending on UserPoolClientLambdaPolicy for easier to understand CFN sequencing
|
||||||
|
Type: 'Custom::LambdaCallout'
|
||||||
|
Properties:
|
||||||
|
ServiceToken: !GetAtt UserPoolClientLambda.Arn
|
||||||
|
clientId: !Ref UserPoolClient
|
||||||
|
userpoolId: !Ref UserPool
|
||||||
|
DependsOn: UserPoolClientLogPolicy
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# BEGIN IDENTITY POOL RESOURCES
|
||||||
|
|
||||||
|
|
||||||
|
IdentityPool:
|
||||||
|
# Always created
|
||||||
|
Type: AWS::Cognito::IdentityPool
|
||||||
|
Properties:
|
||||||
|
IdentityPoolName: !If [ShouldNotCreateEnvResources, 'hpiotwebapp78e5977f_identitypool_78e5977f', !Join ['',['hpiotwebapp78e5977f_identitypool_78e5977f', '__', !Ref env]]]
|
||||||
|
|
||||||
|
CognitoIdentityProviders:
|
||||||
|
- ClientId: !Ref UserPoolClient
|
||||||
|
ProviderName: !Sub
|
||||||
|
- cognito-idp.${region}.amazonaws.com/${client}
|
||||||
|
- { region: !Ref "AWS::Region", client: !Ref UserPool}
|
||||||
|
- ClientId: !Ref UserPoolClientWeb
|
||||||
|
ProviderName: !Sub
|
||||||
|
- cognito-idp.${region}.amazonaws.com/${client}
|
||||||
|
- { region: !Ref "AWS::Region", client: !Ref UserPool}
|
||||||
|
|
||||||
|
AllowUnauthenticatedIdentities: !Ref allowUnauthenticatedIdentities
|
||||||
|
|
||||||
|
|
||||||
|
DependsOn: UserPoolClientInputs
|
||||||
|
|
||||||
|
|
||||||
|
IdentityPoolRoleMap:
|
||||||
|
# Created to map Auth and Unauth roles to the identity pool
|
||||||
|
# Depends on Identity Pool for ID ref
|
||||||
|
Type: AWS::Cognito::IdentityPoolRoleAttachment
|
||||||
|
Properties:
|
||||||
|
IdentityPoolId: !Ref IdentityPool
|
||||||
|
Roles:
|
||||||
|
unauthenticated: !Ref unauthRoleArn
|
||||||
|
authenticated: !Ref authRoleArn
|
||||||
|
DependsOn: IdentityPool
|
||||||
|
|
||||||
|
|
||||||
|
Outputs :
|
||||||
|
|
||||||
|
IdentityPoolId:
|
||||||
|
Value: !Ref 'IdentityPool'
|
||||||
|
Description: Id for the identity pool
|
||||||
|
IdentityPoolName:
|
||||||
|
Value: !GetAtt IdentityPool.Name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
UserPoolId:
|
||||||
|
Value: !Ref 'UserPool'
|
||||||
|
Description: Id for the user pool
|
||||||
|
UserPoolName:
|
||||||
|
Value: !Ref userPoolName
|
||||||
|
AppClientIDWeb:
|
||||||
|
Value: !Ref 'UserPoolClientWeb'
|
||||||
|
Description: The user pool app client id for web
|
||||||
|
AppClientID:
|
||||||
|
Value: !Ref 'UserPoolClient'
|
||||||
|
Description: The user pool app client id
|
||||||
|
AppClientSecret:
|
||||||
|
Value: !GetAtt UserPoolClientInputs.appSecret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
52
amplify/backend/auth/hpiotwebapp78e5977f/parameters.json
Normal file
52
amplify/backend/auth/hpiotwebapp78e5977f/parameters.json
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"identityPoolName": "hpiotwebapp78e5977f_identitypool_78e5977f",
|
||||||
|
"allowUnauthenticatedIdentities": false,
|
||||||
|
"resourceNameTruncated": "hpiotw78e5977f",
|
||||||
|
"userPoolName": "hpiotwebapp78e5977f_userpool_78e5977f",
|
||||||
|
"autoVerifiedAttributes": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"mfaConfiguration": "OFF",
|
||||||
|
"mfaTypes": [
|
||||||
|
"SMS Text Message"
|
||||||
|
],
|
||||||
|
"smsAuthenticationMessage": "Your authentication code is {####}",
|
||||||
|
"smsVerificationMessage": "Your verification code is {####}",
|
||||||
|
"emailVerificationSubject": "Your verification code",
|
||||||
|
"emailVerificationMessage": "Your verification code is {####}",
|
||||||
|
"defaultPasswordPolicy": false,
|
||||||
|
"passwordPolicyMinLength": 8,
|
||||||
|
"passwordPolicyCharacters": [],
|
||||||
|
"requiredAttributes": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"userpoolClientGenerateSecret": true,
|
||||||
|
"userpoolClientRefreshTokenValidity": 30,
|
||||||
|
"userpoolClientWriteAttributes": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"userpoolClientReadAttributes": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"userpoolClientLambdaRole": "hpiotw78e5977f_userpoolclient_lambda_role",
|
||||||
|
"userpoolClientSetAttributes": false,
|
||||||
|
"resourceName": "hpiotwebapp78e5977f",
|
||||||
|
"authSelections": "identityPoolAndUserPool",
|
||||||
|
"authRoleArn": {
|
||||||
|
"Fn::GetAtt": [
|
||||||
|
"AuthRole",
|
||||||
|
"Arn"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unauthRoleArn": {
|
||||||
|
"Fn::GetAtt": [
|
||||||
|
"UnauthRole",
|
||||||
|
"Arn"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"useDefault": "default",
|
||||||
|
"usernameAttributes": [
|
||||||
|
"email, phone_number"
|
||||||
|
],
|
||||||
|
"dependsOn": []
|
||||||
|
}
|
||||||
@@ -4,5 +4,12 @@
|
|||||||
"service": "S3AndCloudFront",
|
"service": "S3AndCloudFront",
|
||||||
"providerPlugin": "awscloudformation"
|
"providerPlugin": "awscloudformation"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"hpiotwebapp78e5977f": {
|
||||||
|
"service": "Cognito",
|
||||||
|
"providerPlugin": "awscloudformation",
|
||||||
|
"dependsOn": []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,5 +10,22 @@
|
|||||||
"StackName": "hpiotwebapp-dev-20191011145158",
|
"StackName": "hpiotwebapp-dev-20191011145158",
|
||||||
"StackId": "arn:aws:cloudformation:us-east-1:860246592755:stack/hpiotwebapp-dev-20191011145158/95829790-ec60-11e9-a33f-1207b4ca758c"
|
"StackId": "arn:aws:cloudformation:us-east-1:860246592755:stack/hpiotwebapp-dev-20191011145158/95829790-ec60-11e9-a33f-1207b4ca758c"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"devs": {
|
||||||
|
"awscloudformation": {
|
||||||
|
"AuthRoleName": "hpiotwebapp-devs-20191011153744-authRole",
|
||||||
|
"UnauthRoleArn": "arn:aws:iam::860246592755:role/hpiotwebapp-devs-20191011153744-unauthRole",
|
||||||
|
"AuthRoleArn": "arn:aws:iam::860246592755:role/hpiotwebapp-devs-20191011153744-authRole",
|
||||||
|
"Region": "us-east-1",
|
||||||
|
"DeploymentBucketName": "hpiotwebapp-devs-20191011153744-deployment",
|
||||||
|
"UnauthRoleName": "hpiotwebapp-devs-20191011153744-unauthRole",
|
||||||
|
"StackName": "hpiotwebapp-devs-20191011153744",
|
||||||
|
"StackId": "arn:aws:cloudformation:us-east-1:860246592755:stack/hpiotwebapp-devs-20191011153744/fa4c3b30-ec66-11e9-8123-0e639cbb91d4"
|
||||||
|
},
|
||||||
|
"categories": {
|
||||||
|
"auth": {
|
||||||
|
"hpiotwebapp78e5977f": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
343
package-lock.json
generated
343
package-lock.json
generated
@@ -1096,6 +1096,167 @@
|
|||||||
"tslib": "^1.9.0"
|
"tslib": "^1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@aws-amplify/analytics": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-amplify/analytics/-/analytics-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-RzSBZ0lIHkOnP88pRc0hjh3z6A+kSfHCTyloUSXrpMgiRMi+XvaWNqD+uH0Au5x9X3xKIZb3AOrsPX9pyCTXeA==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-amplify/cache": "^1.1.2",
|
||||||
|
"@aws-amplify/core": "^1.2.2",
|
||||||
|
"uuid": "^3.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-amplify/api": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-amplify/api/-/api-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-jpMf6HQO36oyZ0WVi6wIDK3EtQCjMLDSwHrLKzFGM83mtcDdC8XBIaqElWG6nKgFE8MKOmG8I9YSinTl3HpPqg==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-amplify/auth": "^1.4.2",
|
||||||
|
"@aws-amplify/cache": "^1.1.2",
|
||||||
|
"@aws-amplify/core": "^1.2.2",
|
||||||
|
"axios": "^0.19.0",
|
||||||
|
"graphql": "0.13.0",
|
||||||
|
"uuid": "^3.2.1",
|
||||||
|
"zen-observable": "^0.8.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-amplify/auth": {
|
||||||
|
"version": "1.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-amplify/auth/-/auth-1.4.2.tgz",
|
||||||
|
"integrity": "sha512-X82t3zxAMjRQjwZ8M4xT+96DhRrb6H8FoZZDp1Pxr4p2KSKKMkEkS2AJCenLNQ37g6CUBLlltQ++iGXDLabskw==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-amplify/cache": "^1.1.2",
|
||||||
|
"@aws-amplify/core": "^1.2.2",
|
||||||
|
"amazon-cognito-identity-js": "^3.1.2",
|
||||||
|
"crypto-js": "^3.1.9-1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-amplify/cache": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-amplify/cache/-/cache-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-ZK/IANuqQ0rrgI2soN6yR6YhwvftHsnmovMXzeQG+tHdnK2n3RxUciku6+Ey9k4usCD67ZFbXbb+V4KWqBf9Yw==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-amplify/core": "^1.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-amplify/core": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-amplify/core/-/core-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-mczpQr+wBP6WxL0EK9yue4p+5mQVm+u8IGiyB9HN5ibMLs1t4hPcbS/D/QzjeWSfZ16oMPvZ3Sej3Z4J9zjv9Q==",
|
||||||
|
"requires": {
|
||||||
|
"aws-sdk": "2.518.0",
|
||||||
|
"url": "^0.11.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-amplify/interactions": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-amplify/interactions/-/interactions-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-07kHROAHk2IF87+3+yWjbagPiQLgDbas3NgqQXg0gxwd/Vt/jsUGHiJop64g4prO7NZ2ut+nx27bf0n3cxPNGA==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-amplify/core": "^1.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-amplify/predictions": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-amplify/predictions/-/predictions-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-vCWy8U4cWbL47C2BxqkeX2JUfH7i9XyVPgFpVorkDKpR4yH4Rt4zyHXzNXT2rP4iFAZwJCVXa1L5QA/u3AxPJg==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-amplify/core": "^1.2.2",
|
||||||
|
"@aws-amplify/storage": "^1.2.2",
|
||||||
|
"@aws-sdk/eventstream-marshaller": "^0.1.0-preview.2",
|
||||||
|
"@aws-sdk/util-utf8-node": "^0.1.0-preview.1",
|
||||||
|
"uuid": "^3.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-amplify/pubsub": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-amplify/pubsub/-/pubsub-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-nyWnf26ppu2VELwtTF6vF8UaDB/SiWau7atwu4MrGBSuLi3GBd5bhvLTPgDWV91YWA8rcc4Q17Cd18LE8guw3Q==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-amplify/core": "^1.2.2",
|
||||||
|
"uuid": "^3.2.1",
|
||||||
|
"zen-observable": "^0.8.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-amplify/storage": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-amplify/storage/-/storage-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-ZZgLRA2yKIfvFfOi2EKBSHE3P2kSsvRu9OX/9uLiHzNfZjF/LNebhpadh3+jMfLvF7obS/AWZYK0wzhZtcgw1Q==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-amplify/core": "^1.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-amplify/ui": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-amplify/ui/-/ui-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-K9wX+yYY0jp+GEDnhT2hVEB5JuLm0a5u134qC+Neg3rD5u71oOUd+jCJq48iuE2hjaNxMMOA0eGwFmsltr7h6w=="
|
||||||
|
},
|
||||||
|
"@aws-amplify/xr": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-amplify/xr/-/xr-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-DSyD12o0rcbvWj0DMEss50HRS3X8xja2lNpNrO8MvjdN6Y8zS+a3GBTM9vx+/kCgp2zRRmC4Ei/ZeD/Rt5N5PA==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-amplify/core": "^1.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-crypto/crc32": {
|
||||||
|
"version": "0.1.0-preview.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-0.1.0-preview.1.tgz",
|
||||||
|
"integrity": "sha512-GPxlpx1ezlWAYygSfyGIsQ2/imDJgOYnpqwAFlU3H4KacIqX3LHpE5/Ps+s7nxiYtEFR9GvQhsN7VNDF5/FbHg==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^1.9.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-sdk/eventstream-marshaller": {
|
||||||
|
"version": "0.1.0-preview.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-marshaller/-/eventstream-marshaller-0.1.0-preview.2.tgz",
|
||||||
|
"integrity": "sha512-StNivqLMGk+6Blp7eBYgLvidD9HEhthzNz7dBBAQPELx3Nd3imodzSvckDw5ZkuWt6ViP+aAl8HgQvJmD71M5Q==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-crypto/crc32": "^0.1.0-preview.1",
|
||||||
|
"@aws-sdk/types": "^0.1.0-preview.1",
|
||||||
|
"@aws-sdk/util-hex-encoding": "^0.1.0-preview.1",
|
||||||
|
"tslib": "^1.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-sdk/is-array-buffer": {
|
||||||
|
"version": "0.1.0-preview.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-0.1.0-preview.1.tgz",
|
||||||
|
"integrity": "sha512-9Qrr9w6sNX19N0eO7JBYjp86OPcOyjDPe580L5ISDKo7XfuzK20IC2TeGTZ77okhRTsm8rF5UgP9scBu59jwoA==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^1.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-sdk/types": {
|
||||||
|
"version": "0.1.0-preview.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-0.1.0-preview.1.tgz",
|
||||||
|
"integrity": "sha512-CcZpxyN2G0I7+Jyj0om3LafYX7d30JWJAAQ+53Ysjau7jyL/xLMMkLZgniQPV8BMV7uKLXyf4hwu8JSs0Ejb+w=="
|
||||||
|
},
|
||||||
|
"@aws-sdk/util-buffer-from": {
|
||||||
|
"version": "0.1.0-preview.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-0.1.0-preview.1.tgz",
|
||||||
|
"integrity": "sha512-i46iuFQA05+92L/epK7befgoxw6DM38LnaHjHNxRoJeIYUllZvpJst74FRCJ5UvVOaMDvHO3woWG+dY8/KVABw==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-sdk/is-array-buffer": "^0.1.0-preview.1",
|
||||||
|
"tslib": "^1.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-sdk/util-hex-encoding": {
|
||||||
|
"version": "0.1.0-preview.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-0.1.0-preview.1.tgz",
|
||||||
|
"integrity": "sha512-97ZMVcJpIXwOQN2RntPimav6G5iod/QHEbqGrmaECXyjXzSrexKHRYjpQAtmJ7geZTMsofoRSdj3qZCymjn7Uw==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^1.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@aws-sdk/util-utf8-node": {
|
||||||
|
"version": "0.1.0-preview.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-0.1.0-preview.1.tgz",
|
||||||
|
"integrity": "sha512-PSUsSJ0nnMPS389f0R3kIVR0BElnEb22Ofj40iO5HCtw9gZ1ot+enFdbOmW4m1e5+ED9U/Hqxqc7QhFWWF4NUQ==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-sdk/util-buffer-from": "^0.1.0-preview.1",
|
||||||
|
"tslib": "^1.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@babel/code-frame": {
|
"@babel/code-frame": {
|
||||||
"version": "7.5.5",
|
"version": "7.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
|
||||||
@@ -1648,6 +1809,16 @@
|
|||||||
"integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
|
"integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"amazon-cognito-identity-js": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/amazon-cognito-identity-js/-/amazon-cognito-identity-js-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-NQ/MxGvzYAauHNWjWu/OMB0LtH5w3D/BKeYP9royQwT6+pR4UJifqFaETh2GQUefROKkv0rqEuvkvT/Wx2RA+Q==",
|
||||||
|
"requires": {
|
||||||
|
"buffer": "4.9.1",
|
||||||
|
"crypto-js": "^3.1.9-1",
|
||||||
|
"js-cookie": "^2.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"amdefine": {
|
"amdefine": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
|
||||||
@@ -1913,6 +2084,90 @@
|
|||||||
"postcss-value-parser": "^4.0.0"
|
"postcss-value-parser": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"aws-amplify": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/aws-amplify/-/aws-amplify-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-HJ+TRwO/zow+F5OBiNaPo4xgCSgR7S4LFJTn9pMB7SqDRm0l8hKFGnlJ2nU7cBxFUf2ot1ZpdIS4l3d6RBKesw==",
|
||||||
|
"requires": {
|
||||||
|
"@aws-amplify/analytics": "^1.3.2",
|
||||||
|
"@aws-amplify/api": "^1.2.2",
|
||||||
|
"@aws-amplify/auth": "^1.4.2",
|
||||||
|
"@aws-amplify/cache": "^1.1.2",
|
||||||
|
"@aws-amplify/core": "^1.2.2",
|
||||||
|
"@aws-amplify/interactions": "^1.1.2",
|
||||||
|
"@aws-amplify/predictions": "^1.1.2",
|
||||||
|
"@aws-amplify/pubsub": "^1.2.2",
|
||||||
|
"@aws-amplify/storage": "^1.2.2",
|
||||||
|
"@aws-amplify/ui": "^1.1.2",
|
||||||
|
"@aws-amplify/xr": "^0.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aws-sdk": {
|
||||||
|
"version": "2.518.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.518.0.tgz",
|
||||||
|
"integrity": "sha512-hwtKKf93TFyd3qugDW54ElpkUXhPe+ArPIHadre6IAFjCJiv08L8DaZKLRyclDnKfTavKe+f/PhdSEYo1QUHiA==",
|
||||||
|
"requires": {
|
||||||
|
"buffer": "4.9.1",
|
||||||
|
"events": "1.1.1",
|
||||||
|
"ieee754": "1.1.8",
|
||||||
|
"jmespath": "0.15.0",
|
||||||
|
"querystring": "0.2.0",
|
||||||
|
"sax": "1.2.1",
|
||||||
|
"url": "0.10.3",
|
||||||
|
"uuid": "3.3.2",
|
||||||
|
"xml2js": "0.4.19"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"events": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
|
||||||
|
},
|
||||||
|
"ieee754": {
|
||||||
|
"version": "1.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
|
||||||
|
"integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
|
||||||
|
},
|
||||||
|
"punycode": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||||
|
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||||
|
},
|
||||||
|
"sax": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
|
||||||
|
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"version": "0.10.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
|
||||||
|
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
|
||||||
|
"requires": {
|
||||||
|
"punycode": "1.3.2",
|
||||||
|
"querystring": "0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
|
||||||
|
},
|
||||||
|
"xml2js": {
|
||||||
|
"version": "0.4.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
|
||||||
|
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
|
||||||
|
"requires": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~9.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xmlbuilder": {
|
||||||
|
"version": "9.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
|
||||||
|
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"aws-sign2": {
|
"aws-sign2": {
|
||||||
"version": "0.7.0",
|
"version": "0.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||||
@@ -1925,6 +2180,38 @@
|
|||||||
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
|
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"axios": {
|
||||||
|
"version": "0.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
|
||||||
|
"integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "1.5.10",
|
||||||
|
"is-buffer": "^2.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"follow-redirects": {
|
||||||
|
"version": "1.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
|
||||||
|
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "=3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is-buffer": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"axobject-query": {
|
"axobject-query": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",
|
||||||
@@ -2147,8 +2434,7 @@
|
|||||||
"base64-js": {
|
"base64-js": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
|
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"base64id": {
|
"base64id": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -2385,7 +2671,6 @@
|
|||||||
"version": "4.9.1",
|
"version": "4.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||||
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
|
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"base64-js": "^1.0.2",
|
"base64-js": "^1.0.2",
|
||||||
"ieee754": "^1.1.4",
|
"ieee754": "^1.1.4",
|
||||||
@@ -3096,6 +3381,11 @@
|
|||||||
"randomfill": "^1.0.3"
|
"randomfill": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"crypto-js": {
|
||||||
|
"version": "3.1.9-1",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz",
|
||||||
|
"integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg="
|
||||||
|
},
|
||||||
"css-parse": {
|
"css-parse": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz",
|
||||||
@@ -4388,6 +4678,14 @@
|
|||||||
"integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==",
|
"integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"graphql": {
|
||||||
|
"version": "0.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/graphql/-/graphql-0.13.0.tgz",
|
||||||
|
"integrity": "sha512-WlO+ZJT9aY3YrBT+H5Kk+eVb3OVVehB9iRD/xqeHdmrrn4AFl5FIcOpfHz/vnBr6Y6JthGMlnFqU8XRnDjSR7A==",
|
||||||
|
"requires": {
|
||||||
|
"iterall": "1.1.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"hammerjs": {
|
"hammerjs": {
|
||||||
"version": "2.0.8",
|
"version": "2.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||||
@@ -4743,8 +5041,7 @@
|
|||||||
"ieee754": {
|
"ieee754": {
|
||||||
"version": "1.1.13",
|
"version": "1.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
|
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"iferr": {
|
"iferr": {
|
||||||
"version": "0.1.5",
|
"version": "0.1.5",
|
||||||
@@ -5175,8 +5472,7 @@
|
|||||||
"isarray": {
|
"isarray": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"isbinaryfile": {
|
"isbinaryfile": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
@@ -5413,6 +5709,11 @@
|
|||||||
"handlebars": "^4.1.2"
|
"handlebars": "^4.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"iterall": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-eaDsM/PY8D/X5mYQhecVc5/9xvSHED7yPON+ffQroBeTuqUVm7dfphMkK8NksXuImqZlVRoKtrNfMIVCYIqaUQ=="
|
||||||
|
},
|
||||||
"jasmine": {
|
"jasmine": {
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz",
|
||||||
@@ -5453,6 +5754,16 @@
|
|||||||
"integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=",
|
"integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"jmespath": {
|
||||||
|
"version": "0.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
|
||||||
|
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
|
||||||
|
},
|
||||||
|
"js-cookie": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ=="
|
||||||
|
},
|
||||||
"js-tokens": {
|
"js-tokens": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||||
@@ -6962,8 +7273,7 @@
|
|||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"multicast-dns": {
|
"multicast-dns": {
|
||||||
"version": "6.2.3",
|
"version": "6.2.3",
|
||||||
@@ -8244,8 +8554,7 @@
|
|||||||
"querystring": {
|
"querystring": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
|
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"querystring-es3": {
|
"querystring-es3": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
@@ -10245,7 +10554,6 @@
|
|||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
|
||||||
"integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
|
"integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"punycode": "1.3.2",
|
"punycode": "1.3.2",
|
||||||
"querystring": "0.2.0"
|
"querystring": "0.2.0"
|
||||||
@@ -10254,8 +10562,7 @@
|
|||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
|
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -10354,8 +10661,7 @@
|
|||||||
"uuid": {
|
"uuid": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
|
||||||
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
|
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"validate-npm-package-license": {
|
"validate-npm-package-license": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
@@ -12192,6 +12498,11 @@
|
|||||||
"integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
|
"integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"zen-observable": {
|
||||||
|
"version": "0.8.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.14.tgz",
|
||||||
|
"integrity": "sha512-kQz39uonEjEESwh+qCi83kcC3rZJGh4mrZW7xjkSQYXkq//JZHTtKo+6yuVloTgMtzsIWOJrjIrKvk/dqm0L5g=="
|
||||||
|
},
|
||||||
"zone.js": {
|
"zone.js": {
|
||||||
"version": "0.9.1",
|
"version": "0.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.9.1.tgz",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"@angular/pwa": "^0.803.9",
|
"@angular/pwa": "^0.803.9",
|
||||||
"@angular/router": "~8.2.0",
|
"@angular/router": "~8.2.0",
|
||||||
"@angular/service-worker": "~8.2.0",
|
"@angular/service-worker": "~8.2.0",
|
||||||
|
"aws-amplify": "^1.2.2",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"rxjs": "~6.4.0",
|
"rxjs": "~6.4.0",
|
||||||
"tslib": "^1.10.0",
|
"tslib": "^1.10.0",
|
||||||
|
|||||||
@@ -1,11 +1,38 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
import { HomeComponent } from './home/home.component';
|
import { HomeComponent } from './home/home.component';
|
||||||
|
import { AuthComponent } from './auth/auth.component';
|
||||||
|
import { SignInComponent } from './auth/sign-in/sign-in.component';
|
||||||
|
import { SignUpComponent } from './auth/sign-up/sign-up.component';
|
||||||
|
import { UnauthGuard } from './auth/unauth.guard';
|
||||||
|
import { AuthGuard } from './auth/auth.guard';
|
||||||
|
import { ConfirmCodeComponent } from './auth/confirm-code/confirm-code.component';
|
||||||
|
import { ProfileComponent } from './auth/profile/profile.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', component: HomeComponent}
|
{ path: 'auth', component: AuthComponent, children: [
|
||||||
];
|
{
|
||||||
|
path: 'signin',
|
||||||
|
component: SignInComponent,
|
||||||
|
canActivate: [UnauthGuard]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'signup',
|
||||||
|
component: SignUpComponent,
|
||||||
|
canActivate: [UnauthGuard]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'confirm',
|
||||||
|
component: ConfirmCodeComponent,
|
||||||
|
canActivate: [UnauthGuard]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'profile',
|
||||||
|
component: ProfileComponent,
|
||||||
|
canActivate: [AuthGuard]
|
||||||
|
}
|
||||||
|
]},
|
||||||
|
{ path: '', component: HomeComponent, canActivate: [AuthGuard] }];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes)],
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ import { MatSidenav } from '@angular/material';
|
|||||||
styleUrls: ['./app.component.scss']
|
styleUrls: ['./app.component.scss']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'Material PWA';
|
title = 'Henry Pump SCADA';
|
||||||
mobileQuery: MediaQueryList;
|
mobileQuery: MediaQueryList;
|
||||||
nav = [
|
nav = [
|
||||||
{
|
{
|
||||||
'title': 'Home',
|
title: 'Home',
|
||||||
'path': '/'
|
path: '/'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'title': 'This is a second link',
|
title: 'Account Settings',
|
||||||
'path': '/auth'
|
path: '/auth'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
private mobileQueryListener: () => void;
|
private mobileQueryListener: () => void;
|
||||||
|
|||||||
@@ -8,20 +8,42 @@ import { MaterialModule } from './material/material.module';
|
|||||||
import { HomeComponent } from './home/home.component';
|
import { HomeComponent } from './home/home.component';
|
||||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
|
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||||
|
import { AuthComponent } from './auth/auth.component';
|
||||||
|
import { LoaderComponent } from './loader/loader.component';
|
||||||
|
import { CountryCodeSelectComponent } from './auth/country-code-select/country-code-select.component';
|
||||||
|
import { FilterPipe } from './auth/country-code-select/filter.pipe';
|
||||||
|
import { SignInComponent } from './auth/sign-in/sign-in.component';
|
||||||
|
import { SignUpComponent } from './auth/sign-up/sign-up.component';
|
||||||
|
import { ConfirmCodeComponent } from './auth/confirm-code/confirm-code.component';
|
||||||
|
import { ProfileComponent } from './auth/profile/profile.component';
|
||||||
|
import { AvatarComponent } from './auth/profile/avatar/avatar.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
HomeComponent
|
HomeComponent,
|
||||||
|
AuthComponent,
|
||||||
|
LoaderComponent,
|
||||||
|
CountryCodeSelectComponent,
|
||||||
|
FilterPipe,
|
||||||
|
SignInComponent,
|
||||||
|
SignUpComponent,
|
||||||
|
ConfirmCodeComponent,
|
||||||
|
ProfileComponent,
|
||||||
|
AvatarComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
MaterialModule,
|
MaterialModule,
|
||||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
|
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormsModule
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent],
|
||||||
|
entryComponents: [LoaderComponent, CountryCodeSelectComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|||||||
2
src/app/auth/auth.component.html
Normal file
2
src/app/auth/auth.component.html
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<router-outlet></router-outlet>
|
||||||
|
|
||||||
9
src/app/auth/auth.component.scss
Normal file
9
src/app/auth/auth.component.scss
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
:host {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-btn-signup {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 1em;
|
||||||
|
right: 2em;
|
||||||
|
}
|
||||||
25
src/app/auth/auth.component.spec.ts
Normal file
25
src/app/auth/auth.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AuthComponent } from './auth.component';
|
||||||
|
|
||||||
|
describe('AuthComponent', () => {
|
||||||
|
let component: AuthComponent;
|
||||||
|
let fixture: ComponentFixture<AuthComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ AuthComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AuthComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
37
src/app/auth/auth.component.ts
Normal file
37
src/app/auth/auth.component.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { Component, OnInit, NgZone } from '@angular/core';
|
||||||
|
import Auth from '@aws-amplify/auth';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { Hub } from '@aws-amplify/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-auth',
|
||||||
|
templateUrl: './auth.component.html',
|
||||||
|
styleUrls: ['./auth.component.scss']
|
||||||
|
})
|
||||||
|
export class AuthComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor( private _router: Router, private _zone: NgZone ) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
Hub.listen('auth', ({ payload: { event, data } }) => {
|
||||||
|
switch (event) {
|
||||||
|
case 'signIn':
|
||||||
|
this._zone.run(() => {
|
||||||
|
this._router.navigate(['/']);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'signOut':
|
||||||
|
this._zone.run(() => {
|
||||||
|
this._router.navigate(['/auth/signin']);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Auth.currentAuthenticatedUser()
|
||||||
|
.then(() => {
|
||||||
|
this._router.navigate(['auth/profile']);
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
src/app/auth/auth.guard.spec.ts
Normal file
15
src/app/auth/auth.guard.spec.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { TestBed, async, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AuthGuard } from './auth.guard';
|
||||||
|
|
||||||
|
describe('AuthGuard', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [AuthGuard]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ...', inject([AuthGuard], (guard: AuthGuard) => {
|
||||||
|
expect(guard).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
||||||
20
src/app/auth/auth.guard.ts
Normal file
20
src/app/auth/auth.guard.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import Auth from '@aws-amplify/auth';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AuthGuard implements CanActivate {
|
||||||
|
constructor( private _router: Router ) { }
|
||||||
|
canActivate(
|
||||||
|
next: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
|
||||||
|
return Auth.currentAuthenticatedUser().then(() => { return true; })
|
||||||
|
.catch(() => {
|
||||||
|
this._router.navigate(['auth/signin']);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/app/auth/auth.service.spec.ts
Normal file
12
src/app/auth/auth.service.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
|
describe('AuthService', () => {
|
||||||
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: AuthService = TestBed.get(AuthService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
72
src/app/auth/auth.service.ts
Normal file
72
src/app/auth/auth.service.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import Auth, { CognitoHostedUIIdentityProvider } from '@aws-amplify/auth';
|
||||||
|
import { Hub, ICredentials } from '@aws-amplify/core';
|
||||||
|
import { Subject, Observable } from 'rxjs';
|
||||||
|
import { CognitoUser } from 'amazon-cognito-identity-js';
|
||||||
|
|
||||||
|
export interface NewUser {
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
password: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AuthService {
|
||||||
|
|
||||||
|
public loggedIn: boolean;
|
||||||
|
private _authState: Subject<CognitoUser|any> = new Subject<CognitoUser|any>();
|
||||||
|
authState: Observable<CognitoUser|any> = this._authState.asObservable();
|
||||||
|
|
||||||
|
public static SIGN_IN = 'signIn';
|
||||||
|
public static SIGN_OUT = 'signOut';
|
||||||
|
public static FACEBOOK = CognitoHostedUIIdentityProvider.Facebook;
|
||||||
|
public static GOOGLE = CognitoHostedUIIdentityProvider.Google;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
Hub.listen('auth', (data) => {
|
||||||
|
const { channel, payload } = data;
|
||||||
|
if (channel === 'auth') {
|
||||||
|
this._authState.next(payload.event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
signUp(user: NewUser): Promise<CognitoUser|any> {
|
||||||
|
return Auth.signUp({
|
||||||
|
"username": user.email,
|
||||||
|
"password": user.password,
|
||||||
|
"attributes": {
|
||||||
|
"email": user.email,
|
||||||
|
"given_name": user.firstName,
|
||||||
|
"family_name": user.lastName,
|
||||||
|
"phone_number": user.phone
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
signIn(username: string, password: string):Promise<CognitoUser|any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
Auth.signIn(username, password)
|
||||||
|
.then((user: CognitoUser|any) => {
|
||||||
|
this.loggedIn = true;
|
||||||
|
resolve(user);
|
||||||
|
}).catch((error: any) => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
signOut(): Promise<any> {
|
||||||
|
return Auth.signOut()
|
||||||
|
.then(() => this.loggedIn = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
socialSignIn(provider:CognitoHostedUIIdentityProvider): Promise<ICredentials> {
|
||||||
|
return Auth.federatedSignIn({
|
||||||
|
'provider': provider
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
12
src/app/auth/confirm-code/confirm-code.component.html
Normal file
12
src/app/auth/confirm-code/confirm-code.component.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<form [formGroup]="confirmForm">
|
||||||
|
<h2>Confirm your email address</h2>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="Email" type="email" formControlName="email">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="Confirmation Code" type="text" formControlName="code" required>
|
||||||
|
<mat-hint *ngIf="!codeInput.value">Enter the confirmation code that was emailed to you</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-raised-button color="primary" (click)="confirmCode()">Confirm</button>
|
||||||
|
<p>Didn't receive a code? <a href="javascript:void(null)" (click)="sendAgain()">Send again</a></p>
|
||||||
|
</form>
|
||||||
25
src/app/auth/confirm-code/confirm-code.component.spec.ts
Normal file
25
src/app/auth/confirm-code/confirm-code.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ConfirmCodeComponent } from './confirm-code.component';
|
||||||
|
|
||||||
|
describe('ConfirmCodeComponent', () => {
|
||||||
|
let component: ConfirmCodeComponent;
|
||||||
|
let fixture: ComponentFixture<ConfirmCodeComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ConfirmCodeComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ConfirmCodeComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
60
src/app/auth/confirm-code/confirm-code.component.ts
Normal file
60
src/app/auth/confirm-code/confirm-code.component.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormGroup, Validators, FormControl } from '@angular/forms';
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import Auth from '@aws-amplify/auth';
|
||||||
|
import { NotificationService } from 'src/app/services/notification.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-confirm-code',
|
||||||
|
templateUrl: './confirm-code.component.html',
|
||||||
|
styleUrls: ['./confirm-code.component.scss']
|
||||||
|
})
|
||||||
|
export class ConfirmCodeComponent implements OnInit {
|
||||||
|
|
||||||
|
email = environment.confirm.email;
|
||||||
|
confirmForm: FormGroup = new FormGroup({
|
||||||
|
email: new FormControl({value: this.email, disabled: true}),
|
||||||
|
code: new FormControl('', [ Validators.required, Validators.min(3) ])
|
||||||
|
});
|
||||||
|
|
||||||
|
get codeInput() { return this.confirmForm.get('code'); }
|
||||||
|
|
||||||
|
constructor( private _router: Router, private _notification: NotificationService ) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (!this.email) {
|
||||||
|
this._router.navigate(['auth/signup']);
|
||||||
|
} else {
|
||||||
|
Auth.resendSignUp(this.email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendAgain() {
|
||||||
|
Auth.resendSignUp(this.email)
|
||||||
|
.then(() => this._notification.show('A code has been emailed to you'))
|
||||||
|
.catch(() => this._notification.show('An error occurred'));
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmCode() {
|
||||||
|
Auth.confirmSignUp(this.email, this.codeInput.value)
|
||||||
|
.then((data: any) => {
|
||||||
|
console.log(data);
|
||||||
|
if (data === 'SUCCESS' &&
|
||||||
|
environment.confirm.email &&
|
||||||
|
environment.confirm.password) {
|
||||||
|
Auth.signIn(this.email, environment.confirm.password)
|
||||||
|
.then(() => {
|
||||||
|
this._router.navigate(['']);
|
||||||
|
}).catch((error: any) => {
|
||||||
|
this._router.navigate(['auth/signin']);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
console.log(error);
|
||||||
|
this._notification.show(error.message);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<mat-form-field class="full-width country-filter">
|
||||||
|
<input matInput [(ngModel)]="searchText" placeholder="Filter Countries">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-nav-list class="country-list">
|
||||||
|
<a mat-list-item *ngFor="let code of countryCodes | filter : searchText" (click)="selectCountry(code)">
|
||||||
|
<span mat-line>{{ code.name }}</span>
|
||||||
|
<span mat-line>{{ code.dial_code }}</span>
|
||||||
|
</a>
|
||||||
|
</mat-nav-list>
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
.country-list {
|
||||||
|
margin-top: 5px;
|
||||||
|
height: 300px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
.country-filter {
|
||||||
|
position: sticky;
|
||||||
|
top: 0em;
|
||||||
|
margin-top:0.5em;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CountryCodeSelectComponent } from './country-code-select.component';
|
||||||
|
|
||||||
|
describe('CountryCodeSelectComponent', () => {
|
||||||
|
let component: CountryCodeSelectComponent;
|
||||||
|
let fixture: ComponentFixture<CountryCodeSelectComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ CountryCodeSelectComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CountryCodeSelectComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { CountryCode, CountryCodes } from './country-codes';
|
||||||
|
import { MatBottomSheetRef } from '@angular/material';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-country-code-select',
|
||||||
|
templateUrl: './country-code-select.component.html',
|
||||||
|
styleUrls: ['./country-code-select.component.scss']
|
||||||
|
})
|
||||||
|
export class CountryCodeSelectComponent implements OnInit {
|
||||||
|
countryCodes: Array<CountryCode> = CountryCodes;
|
||||||
|
constructor(private bottomSheetRef: MatBottomSheetRef<CountryCodeSelectComponent>) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
selectCountry(code: CountryCode) {
|
||||||
|
this.bottomSheetRef.dismiss(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
7
src/app/auth/country-code-select/country-codes.spec.ts
Normal file
7
src/app/auth/country-code-select/country-codes.spec.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { CountryCodes } from './country-codes';
|
||||||
|
|
||||||
|
describe('CountryCodes', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new CountryCodes()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
1213
src/app/auth/country-code-select/country-codes.ts
Normal file
1213
src/app/auth/country-code-select/country-codes.ts
Normal file
File diff suppressed because it is too large
Load Diff
8
src/app/auth/country-code-select/filter.pipe.spec.ts
Normal file
8
src/app/auth/country-code-select/filter.pipe.spec.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { FilterPipe } from './filter.pipe';
|
||||||
|
|
||||||
|
describe('FilterPipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new FilterPipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
19
src/app/auth/country-code-select/filter.pipe.ts
Normal file
19
src/app/auth/country-code-select/filter.pipe.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { CountryCode } from './country-codes';
|
||||||
|
@Pipe({
|
||||||
|
name: 'filter'
|
||||||
|
})
|
||||||
|
export class FilterPipe implements PipeTransform {
|
||||||
|
transform(items: CountryCode[], searchText: string): CountryCode[] {
|
||||||
|
if (!items) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (!searchText) {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
searchText = searchText.toLowerCase();
|
||||||
|
return items.filter( code => {
|
||||||
|
return code.name.toLowerCase().includes(searchText);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/app/auth/profile/avatar/avatar.component.html
Normal file
33
src/app/auth/profile/avatar/avatar.component.html
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<div class="app-avatar">
|
||||||
|
<div class="app-avatar-container">
|
||||||
|
|
||||||
|
<div
|
||||||
|
[class]="previewClass"
|
||||||
|
*ngIf="!hasPhoto"
|
||||||
|
(drop)="onDrop($event)"
|
||||||
|
(dragleave)="onDragout($event)"
|
||||||
|
(dragover)="onDragover($event)"></div>
|
||||||
|
<div class="app-avatar-preview">
|
||||||
|
<img class="app-avatar-preview"
|
||||||
|
src="{{photoUrl}}"
|
||||||
|
*ngIf="hasPhoto"
|
||||||
|
alt="Avatar Preview"
|
||||||
|
(error)="onPhotoError()"
|
||||||
|
/>
|
||||||
|
<button mat-mini-fab (click)="this.removed.emit()" *ngIf="hasPhoto && !uploading" class="avatar-remove" title="Delete avatar" aria-label="Delete avatar"><mat-icon>close</mat-icon></button>
|
||||||
|
<small *ngIf="uploading"><br/>Processing Avatar...</small>
|
||||||
|
</div>
|
||||||
|
<div class="app-upload-input" *ngIf="!uploading && !hasPhoto">
|
||||||
|
<input type="file"
|
||||||
|
accept="image/*"
|
||||||
|
(change)="pick($event)"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="app-alert" *ngIf="errorMessage">
|
||||||
|
<div class="app-alert-body">
|
||||||
|
<span class="app-alert-icon">⚠</span>
|
||||||
|
<div class="app-alert-message">{{ errorMessage }}</div>
|
||||||
|
<a class="app-alert-close" (click)="onAlertClose()">×</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
71
src/app/auth/profile/avatar/avatar.component.scss
Normal file
71
src/app/auth/profile/avatar/avatar.component.scss
Normal file
File diff suppressed because one or more lines are too long
25
src/app/auth/profile/avatar/avatar.component.spec.ts
Normal file
25
src/app/auth/profile/avatar/avatar.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AvatarComponent } from './avatar.component';
|
||||||
|
|
||||||
|
describe('AvatarComponent', () => {
|
||||||
|
let component: AvatarComponent;
|
||||||
|
let fixture: ComponentFixture<AvatarComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ AvatarComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AvatarComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
160
src/app/auth/profile/avatar/avatar.component.ts
Normal file
160
src/app/auth/profile/avatar/avatar.component.ts
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
import Storage from '@aws-amplify/storage';
|
||||||
|
import { NotificationService } from 'src/app/services/notification.service';
|
||||||
|
import { CompressorService } from 'src/app/services/compressor.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-avatar',
|
||||||
|
templateUrl: './avatar.component.html',
|
||||||
|
styleUrls: ['./avatar.component.scss']
|
||||||
|
})
|
||||||
|
export class AvatarComponent {
|
||||||
|
|
||||||
|
photoUrl: string;
|
||||||
|
hasPhoto: boolean = false;
|
||||||
|
uploading: boolean = false;
|
||||||
|
s3ImageFile: any = null;
|
||||||
|
s3ImagePath: string = "avatar";
|
||||||
|
errorMessage: string;
|
||||||
|
previewClass = "app-avatar-upload";
|
||||||
|
|
||||||
|
private _storageOptions: any = { 'level': 'private' };
|
||||||
|
private _previewClassIdle = "app-avatar-upload";
|
||||||
|
private _previewClassOver = "app-avatar-upload-dragover"
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set url(url: string) {
|
||||||
|
this.photoUrl = url;
|
||||||
|
this.hasPhoto = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set storageOptions(storageOptions: any){
|
||||||
|
this._storageOptions = Object.assign(this._storageOptions, storageOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set path(path: string){
|
||||||
|
this.s3ImagePath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set data(data: any) {
|
||||||
|
this.photoUrl = data.url;
|
||||||
|
this.s3ImagePath = data.path;
|
||||||
|
this._storageOptions = Object.assign(this._storageOptions, data.storageOptions);
|
||||||
|
this.hasPhoto = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
picked: EventEmitter<string> = new EventEmitter<string>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
loaded: EventEmitter<string> = new EventEmitter<string>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
uploaded: EventEmitter<Object> = new EventEmitter<Object>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
removed: EventEmitter<null> = new EventEmitter<null>();
|
||||||
|
|
||||||
|
constructor( private notification: NotificationService,
|
||||||
|
private compressor: CompressorService ) {}
|
||||||
|
|
||||||
|
pick(evt) {
|
||||||
|
let file = null;
|
||||||
|
if (evt.target.files) {
|
||||||
|
file = evt.target.files[0];
|
||||||
|
}
|
||||||
|
if (!file && evt.dataTransfer.files) {
|
||||||
|
file = evt.dataTransfer.files[0];
|
||||||
|
}
|
||||||
|
if (!file) { return; }
|
||||||
|
const isImage = file.type.split('/')[0] === 'image';
|
||||||
|
if (!isImage) {
|
||||||
|
this.previewClass = this._previewClassIdle;
|
||||||
|
return this.notification.show('Only images are allowed.');
|
||||||
|
}
|
||||||
|
if (!this._storageOptions.contentType) {
|
||||||
|
this._storageOptions.contentType = file.type;
|
||||||
|
}
|
||||||
|
// console.log('file size: ', file.size);
|
||||||
|
this.picked.emit(file);
|
||||||
|
this.compressor.compress(file).subscribe(
|
||||||
|
(file: File) => {
|
||||||
|
const { name, size, type } = file;
|
||||||
|
// console.log('compressed size: ', size);
|
||||||
|
const fileName = file.name.split('.');
|
||||||
|
const fileExt = fileName[fileName.length - 1];
|
||||||
|
this.s3ImagePath = `${this.s3ImagePath}/picture.${fileExt}`
|
||||||
|
this.s3ImageFile = file;
|
||||||
|
const that = this;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function(e) {
|
||||||
|
const target: any = e.target;
|
||||||
|
const url = target.result;
|
||||||
|
that.photoUrl = url;
|
||||||
|
that.hasPhoto = true;
|
||||||
|
that.loaded.emit(url);
|
||||||
|
that.uploadFile();
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadFile() {
|
||||||
|
this.uploading = true;
|
||||||
|
Storage.put(
|
||||||
|
this.s3ImagePath,
|
||||||
|
this.s3ImageFile, this._storageOptions)
|
||||||
|
.then ( (result:any) => {
|
||||||
|
this.uploaded.emit(result);
|
||||||
|
this.completeFileUpload();
|
||||||
|
})
|
||||||
|
.catch( error => {
|
||||||
|
this.completeFileUpload(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
completeFileUpload(error?:any) {
|
||||||
|
if (error) {
|
||||||
|
return this._setError(error);
|
||||||
|
}
|
||||||
|
this.uploading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onPhotoError() {
|
||||||
|
this.hasPhoto = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onAlertClose() {
|
||||||
|
this._setError(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDrop(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.pick(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragover(event: DragEvent) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
this.previewClass = this._previewClassOver;
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragout(event: DragEvent) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
this.previewClass = this._previewClassIdle;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setError(err) {
|
||||||
|
if (!err) {
|
||||||
|
this.errorMessage = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.errorMessage = err.message || err;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
31
src/app/auth/profile/profile.component.html
Normal file
31
src/app/auth/profile/profile.component.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<div class="container">
|
||||||
|
<h2>My Account</h2>
|
||||||
|
<form [formGroup]="profileForm" autocomplete="on">
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="First Name" type="text" formControlName="fname" autocomplete="family-name" required>
|
||||||
|
<mat-hint *ngIf="!fnameInput.value">Enter your first name</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="Last Name" type="text" formControlName="lname" autocomplete="given-name" required>
|
||||||
|
<mat-hint *ngIf="!lnameInput.value">Enter your last name</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input type="tel" matInput placeholder="Telephone" formControlName="phone" autocomplete="tel">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="profile-picture">
|
||||||
|
<label>Profile Picture</label>
|
||||||
|
<app-avatar
|
||||||
|
[url]="currentAvatarUrl"
|
||||||
|
(loaded)="this.loading.show()"
|
||||||
|
(uploaded)="onAvatarUploadComplete($event)"
|
||||||
|
(removed)="onAvatarRemove()">
|
||||||
|
</app-avatar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button mat-raised-button color="primary" (click)="editProfile()" [disabled]="loading.loading || !profileForm.valid">Save</button>
|
||||||
|
<button mat-raised-button (click)="signOut()" class="signout">Sign Out</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
26
src/app/auth/profile/profile.component.scss
Normal file
26
src/app/auth/profile/profile.component.scss
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
form > * {
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursor-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signout {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 900px) {
|
||||||
|
.container {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 75%;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/app/auth/profile/profile.component.spec.ts
Normal file
25
src/app/auth/profile/profile.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ProfileComponent } from './profile.component';
|
||||||
|
|
||||||
|
describe('ProfileComponent', () => {
|
||||||
|
let component: ProfileComponent;
|
||||||
|
let fixture: ComponentFixture<ProfileComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ProfileComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProfileComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
106
src/app/auth/profile/profile.component.ts
Normal file
106
src/app/auth/profile/profile.component.ts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { AuthService } from '../auth.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { FormGroup, FormControl, Validators } from '@angular/forms';
|
||||||
|
import Auth, { CognitoUser } from '@aws-amplify/auth';
|
||||||
|
import Storage from '@aws-amplify/storage';
|
||||||
|
import { NotificationService } from 'src/app/services/notification.service';
|
||||||
|
import { LoaderService } from 'src/app/loader/loader.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-profile',
|
||||||
|
templateUrl: './profile.component.html',
|
||||||
|
styleUrls: ['./profile.component.scss']
|
||||||
|
})
|
||||||
|
export class ProfileComponent implements OnInit {
|
||||||
|
profileForm: FormGroup = new FormGroup({
|
||||||
|
email: new FormControl('',[ Validators.email ]),
|
||||||
|
phone: new FormControl('', [ Validators.min(10) ]),
|
||||||
|
fname: new FormControl('', [ Validators.min(2) ]),
|
||||||
|
lname: new FormControl('', [ Validators.min(2) ])
|
||||||
|
});
|
||||||
|
currentAvatarUrl: string;
|
||||||
|
avatar: string;
|
||||||
|
deleteAvatar = false;
|
||||||
|
profile:any = {};
|
||||||
|
user: CognitoUser;
|
||||||
|
|
||||||
|
get emailInput() { return this.profileForm.get('email'); }
|
||||||
|
get fnameInput() { return this.profileForm.get('fname'); }
|
||||||
|
get lnameInput() { return this.profileForm.get('lname'); }
|
||||||
|
get phoneInput() { return this.profileForm.get('phone'); }
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private _authService: AuthService,
|
||||||
|
private _router: Router,
|
||||||
|
private _notification: NotificationService,
|
||||||
|
public loading: LoaderService ) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.loading.show();
|
||||||
|
this.getUserInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserInfo() {
|
||||||
|
this.profile = await Auth.currentUserInfo();
|
||||||
|
this.user = await Auth.currentAuthenticatedUser();
|
||||||
|
if ( this.profile.attributes['profile'] ) {
|
||||||
|
this.avatar = this.profile.attributes['profile'];
|
||||||
|
this.currentAvatarUrl = await Storage.vault.get(this.avatar) as string;
|
||||||
|
}
|
||||||
|
this.fnameInput.setValue(this.profile.attributes['given_name']);
|
||||||
|
this.lnameInput.setValue(this.profile.attributes['family_name']);
|
||||||
|
this.phoneInput.setValue(this.profile.attributes['phone_number']);
|
||||||
|
this.loading.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
getEmailInputError() {
|
||||||
|
if (this.emailInput.hasError('email')) {
|
||||||
|
return 'Please enter a valid email address.';
|
||||||
|
}
|
||||||
|
if (this.emailInput.hasError('required')) {
|
||||||
|
return 'An Email is required.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signOut() {
|
||||||
|
this._authService.signOut()
|
||||||
|
.then(() => this._router.navigate(['auth/signin']))
|
||||||
|
}
|
||||||
|
|
||||||
|
onAvatarUploadComplete(data: any) {
|
||||||
|
this.avatar = data.key;
|
||||||
|
this.loading.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
onAvatarRemove() {
|
||||||
|
this.avatar = undefined;
|
||||||
|
this.currentAvatarUrl = undefined;
|
||||||
|
this.deleteAvatar = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async editProfile() {
|
||||||
|
try {
|
||||||
|
let attributes = {
|
||||||
|
'given_name': this.fnameInput.value,
|
||||||
|
'family_name': this.lnameInput.value,
|
||||||
|
'phone_number': this.phoneInput.value
|
||||||
|
};
|
||||||
|
if (this.avatar) {
|
||||||
|
attributes['profile'] = this.avatar;
|
||||||
|
}
|
||||||
|
await Auth.updateUserAttributes(this.user,attributes);
|
||||||
|
if (!this.avatar && this.deleteAvatar) {
|
||||||
|
this.user.deleteAttributes(["profile"],(error) => {
|
||||||
|
if (error) console.log(error);
|
||||||
|
this._notification.show('Your profile information has been updated.');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._notification.show('Your profile information has been updated.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
23
src/app/auth/sign-in/sign-in.component.html
Normal file
23
src/app/auth/sign-in/sign-in.component.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<form [formGroup]="signinForm">
|
||||||
|
<h2>Sign in to your account</h2>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="Email" type="email" formControlName="email" autocomplete="email" required>
|
||||||
|
<mat-hint *ngIf="!emailInput.value">Enter your email address</mat-hint>
|
||||||
|
<mat-error *ngIf="!emailInput.valid">
|
||||||
|
{{getEmailInputError()}}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="Password" [type]="hide ? 'password' : 'text'" formControlName="password" required>
|
||||||
|
<mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility_off' : 'visibility'}}</mat-icon>
|
||||||
|
<mat-hint *ngIf="!passwordInput.value">Enter your password</mat-hint>
|
||||||
|
<mat-error *ngIf="!passwordInput.valid">
|
||||||
|
{{getPasswordInputError()}}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-raised-button color="primary" (click)="signIn()" [disabled]="!signinForm.valid">Sign In</button>
|
||||||
|
<p style="text-align:center;">or</p>
|
||||||
|
<button mat-raised-button color="primary" (click)="signInWithFacebook()">Sign In with Facebook</button><br/><br/>
|
||||||
|
<button mat-raised-button color="primary" (click)="signInWithGoogle()">Sign In with Google</button>
|
||||||
|
<p>Don't have an account? <a routerLink="/auth/signup">Sign up</a></p>
|
||||||
|
</form>
|
||||||
0
src/app/auth/sign-in/sign-in.component.scss
Normal file
0
src/app/auth/sign-in/sign-in.component.scss
Normal file
25
src/app/auth/sign-in/sign-in.component.spec.ts
Normal file
25
src/app/auth/sign-in/sign-in.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SignInComponent } from './sign-in.component';
|
||||||
|
|
||||||
|
describe('SignInComponent', () => {
|
||||||
|
let component: SignInComponent;
|
||||||
|
let fixture: ComponentFixture<SignInComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ SignInComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SignInComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
80
src/app/auth/sign-in/sign-in.component.ts
Normal file
80
src/app/auth/sign-in/sign-in.component.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { FormGroup, FormControl, Validators } from '@angular/forms';
|
||||||
|
import { AuthService } from '../auth.service';
|
||||||
|
import { CognitoUser } from '@aws-amplify/auth';
|
||||||
|
import { NotificationService } from 'src/app/services/notification.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
import { LoaderService } from 'src/app/loader/loader.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-sign-in',
|
||||||
|
templateUrl: './sign-in.component.html',
|
||||||
|
styleUrls: ['./sign-in.component.scss']
|
||||||
|
})
|
||||||
|
export class SignInComponent {
|
||||||
|
|
||||||
|
signinForm: FormGroup = new FormGroup({
|
||||||
|
email: new FormControl('',[ Validators.email, Validators.required ]),
|
||||||
|
password: new FormControl('', [ Validators.required, Validators.min(6) ])
|
||||||
|
});
|
||||||
|
|
||||||
|
hide = true;
|
||||||
|
|
||||||
|
get emailInput() { return this.signinForm.get('email'); }
|
||||||
|
get passwordInput() { return this.signinForm.get('password'); }
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public auth: AuthService,
|
||||||
|
private _notification: NotificationService,
|
||||||
|
private _router: Router,
|
||||||
|
private _loader: LoaderService ) { }
|
||||||
|
|
||||||
|
getEmailInputError() {
|
||||||
|
if (this.emailInput.hasError('email')) {
|
||||||
|
return 'Please enter a valid email address.';
|
||||||
|
}
|
||||||
|
if (this.emailInput.hasError('required')) {
|
||||||
|
return 'An Email is required.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPasswordInputError() {
|
||||||
|
if (this.passwordInput.hasError('required')) {
|
||||||
|
return 'A password is required.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signIn() {
|
||||||
|
this._loader.show();
|
||||||
|
this.auth.signIn(this.emailInput.value, this.passwordInput.value)
|
||||||
|
.then((user: CognitoUser|any) => {
|
||||||
|
this._loader.hide();
|
||||||
|
this._router.navigate(['']);
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
this._loader.hide();
|
||||||
|
this._notification.show(error.message);
|
||||||
|
switch (error.code) {
|
||||||
|
case "UserNotConfirmedException":
|
||||||
|
environment.confirm.email = this.emailInput.value;
|
||||||
|
environment.confirm.password = this.passwordInput.value;
|
||||||
|
this._router.navigate(['auth/confirm']);
|
||||||
|
break;
|
||||||
|
case "UsernameExistsException":
|
||||||
|
this._router.navigate(['auth/signin']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async signInWithFacebook() {
|
||||||
|
const socialResult = await this.auth.socialSignIn(AuthService.FACEBOOK);
|
||||||
|
console.log('fb Result:', socialResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
async signInWithGoogle() {
|
||||||
|
const socialResult = await this.auth.socialSignIn(AuthService.GOOGLE);
|
||||||
|
console.log('google Result:', socialResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/app/auth/sign-up/sign-up.component.html
Normal file
27
src/app/auth/sign-up/sign-up.component.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<form [formGroup]="signupForm">
|
||||||
|
<h2>Create a new account</h2>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="Email" type="email" formControlName="email" autocomplete="email" required>
|
||||||
|
<mat-hint *ngIf="!emailInput.value">Enter your email address</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<span matPrefix>{{ countryCode }} </span>
|
||||||
|
<input type="tel" matInput placeholder="Telephone" formControlName="phone" autocomplete="tel">
|
||||||
|
<mat-icon matSuffix class="cursor-pointer" (click)="selectCountryCode()">mode_edit</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="First Name" type="text" formControlName="fname" autocomplete="given-name" required>
|
||||||
|
<mat-hint *ngIf="!fnameInput.value">Enter your first name</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="Last Name" type="text" formControlName="lname" autocomplete="family-name" required>
|
||||||
|
<mat-hint *ngIf="!lnameInput.value">Enter your last name</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="Password" [type]="hide ? 'password' : 'text'" formControlName="password" required>
|
||||||
|
<mat-icon matSuffix class="cursor-pointer" (click)="hide = !hide">{{hide ? 'visibility_off' : 'visibility'}}</mat-icon>
|
||||||
|
<mat-hint *ngIf="!passwordInput.value">Enter your password</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-raised-button color="primary" (click)="signUp()" [disabled]="shouldEnableSubmit()">Sign up</button>
|
||||||
|
<p class="center">Already have an account? <a routerLink="/auth/signin">Sign In</a></p>
|
||||||
|
</form>
|
||||||
0
src/app/auth/sign-up/sign-up.component.scss
Normal file
0
src/app/auth/sign-up/sign-up.component.scss
Normal file
25
src/app/auth/sign-up/sign-up.component.spec.ts
Normal file
25
src/app/auth/sign-up/sign-up.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SignUpComponent } from './sign-up.component';
|
||||||
|
|
||||||
|
describe('SignUpComponent', () => {
|
||||||
|
let component: SignUpComponent;
|
||||||
|
let fixture: ComponentFixture<SignUpComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ SignUpComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SignUpComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
92
src/app/auth/sign-up/sign-up.component.ts
Normal file
92
src/app/auth/sign-up/sign-up.component.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormGroup, FormControl, Validators } from '@angular/forms';
|
||||||
|
import { MatBottomSheet } from '@angular/material';
|
||||||
|
import { CountryCodeSelectComponent } from '../country-code-select/country-code-select.component';
|
||||||
|
import { CountryCode } from '../country-code-select/country-codes';
|
||||||
|
import { AuthService } from '../auth.service';
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-sign-up',
|
||||||
|
templateUrl: './sign-up.component.html',
|
||||||
|
styleUrls: ['./sign-up.component.scss']
|
||||||
|
})
|
||||||
|
export class SignUpComponent implements OnInit {
|
||||||
|
hide = true;
|
||||||
|
signupForm: FormGroup = new FormGroup({
|
||||||
|
email: new FormControl('', [ Validators.email, Validators.required ]),
|
||||||
|
password: new FormControl('', [ Validators.required ]),
|
||||||
|
phone: new FormControl('', [ Validators.min(10) ]),
|
||||||
|
fname: new FormControl('', [ Validators.min(2) ]),
|
||||||
|
lname: new FormControl('', [ Validators.min(2) ])
|
||||||
|
});
|
||||||
|
|
||||||
|
countryCode = '+1';
|
||||||
|
|
||||||
|
get emailInput() { return this.signupForm.get('email'); }
|
||||||
|
get passwordInput() { return this.signupForm.get('password'); }
|
||||||
|
get fnameInput() { return this.signupForm.get('fname'); }
|
||||||
|
get lnameInput() { return this.signupForm.get('lname'); }
|
||||||
|
get phoneInput() { return this.signupForm.get('phone'); }
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private _bottomSheet: MatBottomSheet,
|
||||||
|
private _authService: AuthService,
|
||||||
|
private _router: Router ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
selectCountryCode() {
|
||||||
|
this._bottomSheet.open(CountryCodeSelectComponent)
|
||||||
|
.afterDismissed()
|
||||||
|
.subscribe(
|
||||||
|
(data: CountryCode) => {
|
||||||
|
this.countryCode = (data) ? data.dial_code : this.countryCode;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getEmailInputError() {
|
||||||
|
if (this.emailInput.hasError('email')) {
|
||||||
|
return 'Please enter a valid email address.';
|
||||||
|
}
|
||||||
|
if (this.emailInput.hasError('required')) {
|
||||||
|
return 'An Email is required.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPasswordInputError() {
|
||||||
|
if (this.passwordInput.hasError('required')) {
|
||||||
|
return 'A password is required.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldEnableSubmit() {
|
||||||
|
return !this.emailInput.valid ||
|
||||||
|
!this.passwordInput.valid ||
|
||||||
|
!this.fnameInput.valid ||
|
||||||
|
!this.lnameInput.valid ||
|
||||||
|
!this.phoneInput.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
signUp() {
|
||||||
|
this._authService.signUp({
|
||||||
|
"email": this.emailInput.value,
|
||||||
|
"password": this.passwordInput.value,
|
||||||
|
"firstName": this.fnameInput.value,
|
||||||
|
"lastName": this.lnameInput.value,
|
||||||
|
"phone": this.countryCode + this.phoneInput.value
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
environment.confirm.email = this.emailInput.value;
|
||||||
|
environment.confirm.password = this.passwordInput.value;
|
||||||
|
this._router.navigate(['auth/confirm']);
|
||||||
|
})
|
||||||
|
.catch((error) => console.log(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
src/app/auth/unauth.guard.spec.ts
Normal file
15
src/app/auth/unauth.guard.spec.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { TestBed, async, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { UnauthGuard } from './unauth.guard';
|
||||||
|
|
||||||
|
describe('UnauthGuard', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [UnauthGuard]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ...', inject([UnauthGuard], (guard: UnauthGuard) => {
|
||||||
|
expect(guard).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
||||||
27
src/app/auth/unauth.guard.ts
Normal file
27
src/app/auth/unauth.guard.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
CanActivate,
|
||||||
|
ActivatedRouteSnapshot,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
Router } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import Auth from '@aws-amplify/auth';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class UnauthGuard implements CanActivate {
|
||||||
|
constructor( private _router: Router ) { }
|
||||||
|
canActivate(
|
||||||
|
next: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
|
||||||
|
return Auth.currentAuthenticatedUser()
|
||||||
|
.then(() => {
|
||||||
|
this._router.navigate(['auth/profile']);
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/app/loader/loader.component.html
Normal file
4
src/app/loader/loader.component.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<div class="loader">
|
||||||
|
<mat-spinner></mat-spinner>
|
||||||
|
<p> {{message}} </p>
|
||||||
|
</div>
|
||||||
7
src/app/loader/loader.component.scss
Normal file
7
src/app/loader/loader.component.scss
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.loader > * {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.loader p {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
25
src/app/loader/loader.component.spec.ts
Normal file
25
src/app/loader/loader.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoaderComponent } from './loader.component';
|
||||||
|
|
||||||
|
describe('LoaderComponent', () => {
|
||||||
|
let component: LoaderComponent;
|
||||||
|
let fixture: ComponentFixture<LoaderComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoaderComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoaderComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
24
src/app/loader/loader.component.ts
Normal file
24
src/app/loader/loader.component.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { Component, OnInit, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA } from '@angular/material';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-loader',
|
||||||
|
templateUrl: './loader.component.html',
|
||||||
|
styleUrls: ['./loader.component.scss']
|
||||||
|
})
|
||||||
|
export class LoaderComponent implements OnInit {
|
||||||
|
|
||||||
|
message = 'Please wait...';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
|
) {
|
||||||
|
if (data.message) {
|
||||||
|
this.message = data.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
12
src/app/loader/loader.service.spec.ts
Normal file
12
src/app/loader/loader.service.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoaderService } from './loader.service';
|
||||||
|
|
||||||
|
describe('LoaderService', () => {
|
||||||
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: LoaderService = TestBed.get(LoaderService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
30
src/app/loader/loader.service.ts
Normal file
30
src/app/loader/loader.service.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { MatDialog, MatDialogRef } from '@angular/material';
|
||||||
|
import { LoaderComponent } from './loader.component';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class LoaderService {
|
||||||
|
loading: boolean;
|
||||||
|
dialogRef: MatDialogRef<LoaderComponent>;
|
||||||
|
constructor( private _dialog: MatDialog ) { }
|
||||||
|
|
||||||
|
show(message: string = "Please wait..."): void {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loading = true;
|
||||||
|
this.dialogRef = this._dialog.open(LoaderComponent, {
|
||||||
|
width: '80%',
|
||||||
|
data: { 'message': message },
|
||||||
|
closeOnNavigation: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
if (this.dialogRef) {
|
||||||
|
this.dialogRef.close();
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/app/services/compressor.service.spec.ts
Normal file
12
src/app/services/compressor.service.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CompressorService } from './compressor.service';
|
||||||
|
|
||||||
|
describe('CompressorService', () => {
|
||||||
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: CompressorService = TestBed.get(CompressorService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
41
src/app/services/compressor.service.ts
Normal file
41
src/app/services/compressor.service.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable} from 'rxjs';
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class CompressorService {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
compress(file: File): Observable<any> {
|
||||||
|
const width = 128; // For scaling relative to width
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
return new Observable(observer => {
|
||||||
|
reader.onload = ev => {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = (ev.target as any).result;
|
||||||
|
(img.onload = () => {
|
||||||
|
const elem = document.createElement('canvas'); // Use Angular's Renderer2 method
|
||||||
|
const scaleFactor = width / img.width;
|
||||||
|
elem.width = width;
|
||||||
|
elem.height = img.height * scaleFactor;
|
||||||
|
const ctx = elem.getContext('2d') as CanvasRenderingContext2D;
|
||||||
|
ctx.drawImage(img, 0, 0, width, img.height * scaleFactor);
|
||||||
|
ctx.canvas.toBlob(
|
||||||
|
blob => {
|
||||||
|
observer.next(
|
||||||
|
new File([blob], file.name, {
|
||||||
|
type: file.type,
|
||||||
|
lastModified: Date.now(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
file.type,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
(reader.onerror = error => observer.error(error));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/app/services/notification.service.spec.ts
Normal file
12
src/app/services/notification.service.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { NotificationService } from './notification.service';
|
||||||
|
|
||||||
|
describe('NotificationService', () => {
|
||||||
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: NotificationService = TestBed.get(NotificationService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
57
src/app/services/notification.service.ts
Normal file
57
src/app/services/notification.service.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { Injectable, OnDestroy } from '@angular/core';
|
||||||
|
import { MatSnackBar, MatSnackBarRef } from '@angular/material';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an abstract wrapper around showing a MatSnackbar
|
||||||
|
* notification based on global environment or API provided
|
||||||
|
* configuration.
|
||||||
|
*
|
||||||
|
* This class Listens for the authentication state to change.
|
||||||
|
* Once the state becomes authenticated, retrieve the startup
|
||||||
|
* configuration from the API service. Once de-authenticated
|
||||||
|
* set the _params to undefined and unsubscribe.
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class NotificationService implements OnDestroy {
|
||||||
|
|
||||||
|
// Configuration api subscription
|
||||||
|
private _configState: Subscription;
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param toast {MatSnackBar}
|
||||||
|
* @param configService {ConfigurationService}
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private toast: MatSnackBar) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from the config state
|
||||||
|
* when the component is destroyed, and remove
|
||||||
|
* the in-memory parameters.
|
||||||
|
*/
|
||||||
|
ngOnDestroy() {
|
||||||
|
this._configState.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a MatSnackbar notification and return the reference.
|
||||||
|
* Will set the duration to the global configuration if present.
|
||||||
|
* @param message {string}
|
||||||
|
* @param buttonLabel {string}
|
||||||
|
* @returns {MatSnackBarRef}
|
||||||
|
*/
|
||||||
|
show(message: string, buttonLabel: string = 'OK'): MatSnackBarRef<any> {
|
||||||
|
const toastTimeout = 8000;
|
||||||
|
if (toastTimeout > 0) {
|
||||||
|
return this.toast.open(message, buttonLabel, {
|
||||||
|
duration: toastTimeout
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return this.toast.open(message, buttonLabel, {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,11 @@
|
|||||||
// The list of file replacements can be found in `angular.json`.
|
// The list of file replacements can be found in `angular.json`.
|
||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false
|
production: false,
|
||||||
|
confirm: {
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>HPIoTWebApp</title>
|
<title>Henry Pump SCADA</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|||||||
import { AppModule } from './app/app.module';
|
import { AppModule } from './app/app.module';
|
||||||
import { environment } from './environments/environment';
|
import { environment } from './environments/environment';
|
||||||
|
|
||||||
|
// Amplify Config
|
||||||
|
import Auth from '@aws-amplify/auth';
|
||||||
|
import AWSConfig from './aws-exports';
|
||||||
|
|
||||||
|
Auth.configure(AWSConfig);
|
||||||
|
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
/***************************************************************************************************
|
/***************************************************************************************************
|
||||||
* Zone JS is required by default for Angular itself.
|
* Zone JS is required by default for Angular itself.
|
||||||
*/
|
*/
|
||||||
|
(window as any).global = window;
|
||||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./out-tsc/app",
|
"outDir": "./out-tsc/app",
|
||||||
"types": []
|
"types": ["node"]
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src/main.ts",
|
"src/main.ts",
|
||||||
|
|||||||
Reference in New Issue
Block a user