From a2a6b3cdf1583dfcf622de6571cf041c22aa6866 Mon Sep 17 00:00:00 2001 From: khairikz Date: Tue, 7 May 2024 23:25:06 +0800 Subject: [PATCH 1/6] add amplify --- .gitignore | 22 +++++++ amplify/.config/project-config.json | 17 +++++ amplify/README.md | 8 +++ .../cli-inputs.json | 45 +++++++++++++ amplify/backend/backend-config.json | 28 +++++++++ amplify/backend/tags.json | 10 +++ .../amplify-dependent-resources-ref.d.ts | 13 ++++ amplify/cli.json | 63 +++++++++++++++++++ amplify/hooks/README.md | 7 +++ amplify/team-provider-info.json | 20 ++++++ package.json | 2 + src/App.vue | 10 ++- src/_nav.js | 6 ++ src/main.js | 4 ++ 14 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 amplify/.config/project-config.json create mode 100644 amplify/README.md create mode 100644 amplify/backend/auth/coreuifreevueadmintec6cf2a16/cli-inputs.json create mode 100644 amplify/backend/backend-config.json create mode 100644 amplify/backend/tags.json create mode 100644 amplify/backend/types/amplify-dependent-resources-ref.d.ts create mode 100644 amplify/cli.json create mode 100644 amplify/hooks/README.md create mode 100644 amplify/team-provider-info.json diff --git a/.gitignore b/.gitignore index 74821944..2698720d 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,25 @@ Thumbs.db *.vi *.zip *~ + +#amplify-do-not-edit-begin +amplify/\#current-cloud-backend +amplify/.config/local-* +amplify/logs +amplify/mock-data +amplify/mock-api-resources +amplify/backend/amplify-meta.json +amplify/backend/.temp +build/ +dist/ +node_modules/ +aws-exports.js +awsconfiguration.json +amplifyconfiguration.json +amplifyconfiguration.dart +amplify-build-config.json +amplify-gradle-config.json +amplifytools.xcconfig +.secret-* +**.sample +#amplify-do-not-edit-end diff --git a/amplify/.config/project-config.json b/amplify/.config/project-config.json new file mode 100644 index 00000000..21b7ad03 --- /dev/null +++ b/amplify/.config/project-config.json @@ -0,0 +1,17 @@ +{ + "projectName": "coreuifreevueadminte", + "version": "3.1", + "frontend": "javascript", + "javascript": { + "framework": "vue", + "config": { + "SourceDir": "src", + "DistributionDir": "dist", + "BuildCommand": "npm run-script build", + "StartCommand": "npm run-script serve" + } + }, + "providers": [ + "awscloudformation" + ] +} \ No newline at end of file diff --git a/amplify/README.md b/amplify/README.md new file mode 100644 index 00000000..46165a9c --- /dev/null +++ b/amplify/README.md @@ -0,0 +1,8 @@ +# Getting Started with Amplify CLI +This directory was generated by [Amplify CLI](https://docs.amplify.aws/cli). + +Helpful resources: +- Amplify documentation: https://docs.amplify.aws. +- Amplify CLI documentation: https://docs.amplify.aws/cli. +- More details on this folder & generated files: https://docs.amplify.aws/cli/reference/files. +- Join Amplify's community: https://amplify.aws/community/. diff --git a/amplify/backend/auth/coreuifreevueadmintec6cf2a16/cli-inputs.json b/amplify/backend/auth/coreuifreevueadmintec6cf2a16/cli-inputs.json new file mode 100644 index 00000000..d27f8252 --- /dev/null +++ b/amplify/backend/auth/coreuifreevueadmintec6cf2a16/cli-inputs.json @@ -0,0 +1,45 @@ +{ + "version": "1", + "cognitoConfig": { + "identityPoolName": "coreuifreevueadmintec6cf2a16_identitypool_c6cf2a16", + "allowUnauthenticatedIdentities": false, + "resourceNameTruncated": "coreuic6cf2a16", + "userPoolName": "coreuifreevueadmintec6cf2a16_userpool_c6cf2a16", + "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" + ], + "aliasAttributes": [], + "userpoolClientGenerateSecret": false, + "userpoolClientRefreshTokenValidity": 30, + "userpoolClientWriteAttributes": [ + "email" + ], + "userpoolClientReadAttributes": [ + "email" + ], + "userpoolClientLambdaRole": "coreuic6cf2a16_userpoolclient_lambda_role", + "userpoolClientSetAttributes": false, + "sharedId": "c6cf2a16", + "resourceName": "coreuifreevueadmintec6cf2a16", + "authSelections": "identityPoolAndUserPool", + "useDefault": "default", + "userPoolGroupList": [], + "serviceName": "Cognito", + "usernameCaseSensitive": false, + "useEnabledMfas": true + } +} \ No newline at end of file diff --git a/amplify/backend/backend-config.json b/amplify/backend/backend-config.json new file mode 100644 index 00000000..4e621295 --- /dev/null +++ b/amplify/backend/backend-config.json @@ -0,0 +1,28 @@ +{ + "auth": { + "coreuifreevueadmintec6cf2a16": { + "customAuth": false, + "dependsOn": [], + "frontendAuthConfig": { + "mfaConfiguration": "OFF", + "mfaTypes": [ + "SMS" + ], + "passwordProtectionSettings": { + "passwordPolicyCharacters": [], + "passwordPolicyMinLength": 8 + }, + "signupAttributes": [ + "EMAIL" + ], + "socialProviders": [], + "usernameAttributes": [], + "verificationMechanisms": [ + "EMAIL" + ] + }, + "providerPlugin": "awscloudformation", + "service": "Cognito" + } + } +} \ No newline at end of file diff --git a/amplify/backend/tags.json b/amplify/backend/tags.json new file mode 100644 index 00000000..b9321d71 --- /dev/null +++ b/amplify/backend/tags.json @@ -0,0 +1,10 @@ +[ + { + "Key": "user:Stack", + "Value": "{project-env}" + }, + { + "Key": "user:Application", + "Value": "{project-name}" + } +] \ No newline at end of file diff --git a/amplify/backend/types/amplify-dependent-resources-ref.d.ts b/amplify/backend/types/amplify-dependent-resources-ref.d.ts new file mode 100644 index 00000000..a00de852 --- /dev/null +++ b/amplify/backend/types/amplify-dependent-resources-ref.d.ts @@ -0,0 +1,13 @@ +export type AmplifyDependentResourcesAttributes = { + "auth": { + "coreuifreevueadmintec6cf2a16": { + "AppClientID": "string", + "AppClientIDWeb": "string", + "IdentityPoolId": "string", + "IdentityPoolName": "string", + "UserPoolArn": "string", + "UserPoolId": "string", + "UserPoolName": "string" + } + } +} \ No newline at end of file diff --git a/amplify/cli.json b/amplify/cli.json new file mode 100644 index 00000000..1058d7b0 --- /dev/null +++ b/amplify/cli.json @@ -0,0 +1,63 @@ +{ + "features": { + "graphqltransformer": { + "addmissingownerfields": true, + "improvepluralization": false, + "validatetypenamereservedwords": true, + "useexperimentalpipelinedtransformer": true, + "enableiterativegsiupdates": true, + "secondarykeyasgsi": true, + "skipoverridemutationinputtypes": true, + "transformerversion": 2, + "suppressschemamigrationprompt": true, + "securityenhancementnotification": false, + "showfieldauthnotification": false, + "usesubusernamefordefaultidentityclaim": true, + "usefieldnameforprimarykeyconnectionfield": false, + "enableautoindexquerynames": true, + "respectprimarykeyattributesonconnectionfield": true, + "shoulddeepmergedirectiveconfigdefaults": false, + "populateownerfieldforstaticgroupauth": true + }, + "frontend-ios": { + "enablexcodeintegration": true + }, + "auth": { + "enablecaseinsensitivity": true, + "useinclusiveterminology": true, + "breakcirculardependency": true, + "forcealiasattributes": false, + "useenabledmfas": true + }, + "codegen": { + "useappsyncmodelgenplugin": true, + "usedocsgeneratorplugin": true, + "usetypesgeneratorplugin": true, + "cleangeneratedmodelsdirectory": true, + "retaincasestyle": true, + "addtimestampfields": true, + "handlelistnullabilitytransparently": true, + "emitauthprovider": true, + "generateindexrules": true, + "enabledartnullsafety": true, + "generatemodelsforlazyloadandcustomselectionset": false + }, + "appsync": { + "generategraphqlpermissions": true + }, + "latestregionsupport": { + "pinpoint": 1, + "translate": 1, + "transcribe": 1, + "rekognition": 1, + "textract": 1, + "comprehend": 1 + }, + "project": { + "overrides": true + } + }, + "debug": { + "shareProjectConfig": false + } +} \ No newline at end of file diff --git a/amplify/hooks/README.md b/amplify/hooks/README.md new file mode 100644 index 00000000..8fb601ea --- /dev/null +++ b/amplify/hooks/README.md @@ -0,0 +1,7 @@ +# Command Hooks + +Command hooks can be used to run custom scripts upon Amplify CLI lifecycle events like pre-push, post-add-function, etc. + +To get started, add your script files based on the expected naming convention in this directory. + +Learn more about the script file naming convention, hook parameters, third party dependencies, and advanced configurations at https://docs.amplify.aws/cli/usage/command-hooks diff --git a/amplify/team-provider-info.json b/amplify/team-provider-info.json new file mode 100644 index 00000000..f2243b0a --- /dev/null +++ b/amplify/team-provider-info.json @@ -0,0 +1,20 @@ +{ + "dev": { + "awscloudformation": { + "AuthRoleName": "amplify-coreuifreevueadminte-dev-3084a-authRole", + "UnauthRoleArn": "arn:aws:iam::590183969838:role/amplify-coreuifreevueadminte-dev-3084a-unauthRole", + "AuthRoleArn": "arn:aws:iam::590183969838:role/amplify-coreuifreevueadminte-dev-3084a-authRole", + "Region": "ap-southeast-1", + "DeploymentBucketName": "amplify-coreuifreevueadminte-dev-3084a-deployment", + "UnauthRoleName": "amplify-coreuifreevueadminte-dev-3084a-unauthRole", + "StackName": "amplify-coreuifreevueadminte-dev-3084a", + "StackId": "arn:aws:cloudformation:ap-southeast-1:590183969838:stack/amplify-coreuifreevueadminte-dev-3084a/50c48fd0-0c79-11ef-b93d-06c8b6197539", + "AmplifyAppId": "d3m5s31qhs79o0" + }, + "categories": { + "auth": { + "coreuifreevueadmintec6cf2a16": {} + } + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index 3b5b531a..0d17b420 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "preview": "vite preview" }, "dependencies": { + "@aws-amplify/ui-vue": "^4.2.6", "@coreui/chartjs": "^4.0.0", "@coreui/coreui": "^5.0.0", "@coreui/icons": "^3.0.1", @@ -26,6 +27,7 @@ "@coreui/vue": "^5.0.0", "@coreui/vue-chartjs": "^3.0.0", "@popperjs/core": "^2.11.8", + "aws-amplify": "^6.2.0", "chart.js": "^4.4.2", "pinia": "^2.1.7", "simplebar-vue": "^2.3.3", diff --git a/src/App.vue b/src/App.vue index b06f287b..0f383582 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,4 +1,6 @@ \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index f70246f6..cfac90b7 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -21,6 +21,26 @@ const routes = [ /* webpackChunkName: "dashboard" */ '@/views/dashboard/Dashboard.vue' ), }, + { + path: '/exam', + name: 'Exam', + redirect: '/exam/resultlist', + }, + { + path: '/exam/resultlist', + name: 'Result List', + component: () => import('@/views/exam/ResultList.vue'), + }, + { + path: '/exam/submission', + name: 'Submission', + component: () => import('@/views/exam/Submission.vue'), + }, + { + path: '/exam/myresult', + name: 'My Result', + component: () => import('@/views/exam/Result.vue'), + }, { path: '/theme', name: 'Theme', From 946999bcb2d449d27f0485a73d4506084df02a3d Mon Sep 17 00:00:00 2001 From: khairikz Date: Wed, 8 May 2024 21:49:53 +0800 Subject: [PATCH 3/6] upload missing untracked files --- .../backend/api/ab3examapi/cli-inputs.json | 12 + amplify/backend/api/ab3snsapi/cli-inputs.json | 12 + .../ab3examapi-cloudformation-template.json | 238 +++++++++++++++++ .../backend/function/ab3examapi/amplify.state | 6 + .../function/ab3examapi/custom-policies.json | 6 + .../ab3examapi/function-parameters.json | 3 + .../backend/function/ab3examapi/src/app.js | 250 ++++++++++++++++++ .../function/ab3examapi/src/event.json | 11 + .../backend/function/ab3examapi/src/index.js | 15 ++ .../function/ab3examapi/src/package.json | 19 ++ .../storage/ab3dynamodb/cli-inputs.json | 14 + src/assets/AElogo.svg | 28 ++ src/views/exam/Exam.vue | 81 ++++++ src/views/exam/Result.vue | 95 +++++++ src/views/exam/ResultList.vue | 81 ++++++ src/views/exam/Submission.vue | 108 ++++++++ src/views/exam/students.js | 18 ++ 17 files changed, 997 insertions(+) create mode 100644 amplify/backend/api/ab3examapi/cli-inputs.json create mode 100644 amplify/backend/api/ab3snsapi/cli-inputs.json create mode 100644 amplify/backend/function/ab3examapi/ab3examapi-cloudformation-template.json create mode 100644 amplify/backend/function/ab3examapi/amplify.state create mode 100644 amplify/backend/function/ab3examapi/custom-policies.json create mode 100644 amplify/backend/function/ab3examapi/function-parameters.json create mode 100644 amplify/backend/function/ab3examapi/src/app.js create mode 100644 amplify/backend/function/ab3examapi/src/event.json create mode 100644 amplify/backend/function/ab3examapi/src/index.js create mode 100644 amplify/backend/function/ab3examapi/src/package.json create mode 100644 amplify/backend/storage/ab3dynamodb/cli-inputs.json create mode 100644 src/assets/AElogo.svg create mode 100644 src/views/exam/Exam.vue create mode 100644 src/views/exam/Result.vue create mode 100644 src/views/exam/ResultList.vue create mode 100644 src/views/exam/Submission.vue create mode 100644 src/views/exam/students.js diff --git a/amplify/backend/api/ab3examapi/cli-inputs.json b/amplify/backend/api/ab3examapi/cli-inputs.json new file mode 100644 index 00000000..0b916e1f --- /dev/null +++ b/amplify/backend/api/ab3examapi/cli-inputs.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "paths": { + "/exam": { + "name": "/exam", + "lambdaFunction": "ab3examapi", + "permissions": { + "setting": "open" + } + } + } +} \ No newline at end of file diff --git a/amplify/backend/api/ab3snsapi/cli-inputs.json b/amplify/backend/api/ab3snsapi/cli-inputs.json new file mode 100644 index 00000000..ea9493f6 --- /dev/null +++ b/amplify/backend/api/ab3snsapi/cli-inputs.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "paths": { + "/sns": { + "name": "/sns", + "lambdaFunction": "ab3snsapi", + "permissions": { + "setting": "open" + } + } + } +} \ No newline at end of file diff --git a/amplify/backend/function/ab3examapi/ab3examapi-cloudformation-template.json b/amplify/backend/function/ab3examapi/ab3examapi-cloudformation-template.json new file mode 100644 index 00000000..20b21cc0 --- /dev/null +++ b/amplify/backend/function/ab3examapi/ab3examapi-cloudformation-template.json @@ -0,0 +1,238 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.11.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}", + "Parameters": { + "CloudWatchRule": { + "Type": "String", + "Default": "NONE", + "Description": " Schedule Expression" + }, + "deploymentBucketName": { + "Type": "String" + }, + "env": { + "Type": "String" + }, + "s3Key": { + "Type": "String" + }, + "storageab3dynamodbName": { + "Type": "String", + "Default": "storageab3dynamodbName" + }, + "storageab3dynamodbArn": { + "Type": "String", + "Default": "storageab3dynamodbArn" + } + }, + "Conditions": { + "ShouldNotCreateEnvResources": { + "Fn::Equals": [ + { + "Ref": "env" + }, + "NONE" + ] + } + }, + "Resources": { + "LambdaFunction": { + "Type": "AWS::Lambda::Function", + "Metadata": { + "aws:asset:path": "./src", + "aws:asset:property": "Code" + }, + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "deploymentBucketName" + }, + "S3Key": { + "Ref": "s3Key" + } + }, + "Handler": "index.handler", + "FunctionName": { + "Fn::If": [ + "ShouldNotCreateEnvResources", + "ab3examapi", + { + "Fn::Join": [ + "", + [ + "ab3examapi", + "-", + { + "Ref": "env" + } + ] + ] + } + ] + }, + "Environment": { + "Variables": { + "ENV": { + "Ref": "env" + }, + "REGION": { + "Ref": "AWS::Region" + } + } + }, + "Role": { + "Fn::GetAtt": [ + "LambdaExecutionRole", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "Layers": [], + "Timeout": 25 + } + }, + "LambdaExecutionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "RoleName": { + "Fn::If": [ + "ShouldNotCreateEnvResources", + "coreuifreevueadminteLambdaRolefa7ce4c8", + { + "Fn::Join": [ + "", + [ + "coreuifreevueadminteLambdaRolefa7ce4c8", + "-", + { + "Ref": "env" + } + ] + ] + } + ] + }, + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + }, + "Action": [ + "sts:AssumeRole" + ] + } + ] + } + } + }, + "lambdaexecutionpolicy": { + "DependsOn": [ + "LambdaExecutionRole" + ], + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyName": "lambda-execution-policy", + "Roles": [ + { + "Ref": "LambdaExecutionRole" + } + ], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource": { + "Fn::Sub": [ + "arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*", + { + "region": { + "Ref": "AWS::Region" + }, + "account": { + "Ref": "AWS::AccountId" + }, + "lambda": { + "Ref": "LambdaFunction" + } + } + ] + } + }, + { + "Effect": "Allow", + "Action": [ + "dynamodb:DescribeTable", + "dynamodb:GetItem", + "dynamodb:Query", + "dynamodb:Scan", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem" + ], + "Resource": [ + { + "Ref": "storageab3dynamodbArn" + }, + { + "Fn::Join": [ + "/", + [ + { + "Ref": "storageab3dynamodbArn" + }, + "index/*" + ] + ] + } + ] + } + ] + } + } + } + }, + "Outputs": { + "Name": { + "Value": { + "Ref": "LambdaFunction" + } + }, + "Arn": { + "Value": { + "Fn::GetAtt": [ + "LambdaFunction", + "Arn" + ] + } + }, + "Region": { + "Value": { + "Ref": "AWS::Region" + } + }, + "LambdaExecutionRole": { + "Value": { + "Ref": "LambdaExecutionRole" + } + }, + "LambdaExecutionRoleArn": { + "Value": { + "Fn::GetAtt": [ + "LambdaExecutionRole", + "Arn" + ] + } + } + } +} \ No newline at end of file diff --git a/amplify/backend/function/ab3examapi/amplify.state b/amplify/backend/function/ab3examapi/amplify.state new file mode 100644 index 00000000..2a685b3b --- /dev/null +++ b/amplify/backend/function/ab3examapi/amplify.state @@ -0,0 +1,6 @@ +{ + "pluginId": "amplify-nodejs-function-runtime-provider", + "functionRuntime": "nodejs", + "useLegacyBuild": true, + "defaultEditorFile": "src/app.js" +} \ No newline at end of file diff --git a/amplify/backend/function/ab3examapi/custom-policies.json b/amplify/backend/function/ab3examapi/custom-policies.json new file mode 100644 index 00000000..528c94f2 --- /dev/null +++ b/amplify/backend/function/ab3examapi/custom-policies.json @@ -0,0 +1,6 @@ +[ + { + "Action": [], + "Resource": [] + } +] \ No newline at end of file diff --git a/amplify/backend/function/ab3examapi/function-parameters.json b/amplify/backend/function/ab3examapi/function-parameters.json new file mode 100644 index 00000000..d5078776 --- /dev/null +++ b/amplify/backend/function/ab3examapi/function-parameters.json @@ -0,0 +1,3 @@ +{ + "lambdaLayers": [] +} \ No newline at end of file diff --git a/amplify/backend/function/ab3examapi/src/app.js b/amplify/backend/function/ab3examapi/src/app.js new file mode 100644 index 00000000..cff17913 --- /dev/null +++ b/amplify/backend/function/ab3examapi/src/app.js @@ -0,0 +1,250 @@ +/* +Copyright 2017 - 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at + http://aws.amazon.com/apache2.0/ +or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and limitations under the License. +*/ + + + +const { DynamoDBClient } = require('@aws-sdk/client-dynamodb'); +const { DeleteCommand, DynamoDBDocumentClient, GetCommand, PutCommand, QueryCommand, ScanCommand } = require('@aws-sdk/lib-dynamodb'); +const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware') +const bodyParser = require('body-parser') +const express = require('express') + +const ddbClient = new DynamoDBClient({ region: process.env.TABLE_REGION }); +const ddbDocClient = DynamoDBDocumentClient.from(ddbClient); + +let tableName = "ab3examtable"; +if (process.env.ENV && process.env.ENV !== "NONE") { + tableName = tableName + '-' + process.env.ENV; +} + +const userIdPresent = false; // TODO: update in case is required to use that definition +const partitionKeyName = "id"; +const partitionKeyType = "N"; +const sortKeyName = "subject"; +const sortKeyType = "S"; +const hasSortKey = sortKeyName !== ""; +const path = "/exam"; +const UNAUTH = 'UNAUTH'; +const hashKeyPath = '/:' + partitionKeyName; +const sortKeyPath = hasSortKey ? '/:' + sortKeyName : ''; + +// declare a new express app +const app = express() +app.use(bodyParser.json()) +app.use(awsServerlessExpressMiddleware.eventContext()) + +// Enable CORS for all methods +app.use(function(req, res, next) { + res.header("Access-Control-Allow-Origin", "*") + res.header("Access-Control-Allow-Headers", "*") + next() +}); + +// convert url string param to expected Type +const convertUrlType = (param, type) => { + switch(type) { + case "N": + return Number.parseInt(param); + default: + return param; + } +} + +/************************************ +* HTTP Get method to list objects * +************************************/ + +app.get(path, async function(req, res) { + var params = { + TableName: tableName, + Select: 'ALL_ATTRIBUTES', + }; + + try { + const data = await ddbDocClient.send(new ScanCommand(params)); + res.json(data.Items); + } catch (err) { + res.statusCode = 500; + res.json({error: 'Could not load items: ' + err.message}); + } +}); + +/************************************ + * HTTP Get method to query objects * + ************************************/ + +app.get(path + hashKeyPath, async function(req, res) { + const condition = {} + condition[partitionKeyName] = { + ComparisonOperator: 'EQ' + } + + if (userIdPresent && req.apiGateway) { + condition[partitionKeyName]['AttributeValueList'] = [req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH ]; + } else { + try { + condition[partitionKeyName]['AttributeValueList'] = [ convertUrlType(req.params[partitionKeyName], partitionKeyType) ]; + } catch(err) { + res.statusCode = 500; + res.json({error: 'Wrong column type ' + err}); + } + } + + let queryParams = { + TableName: tableName, + KeyConditions: condition + } + + try { + const data = await ddbDocClient.send(new QueryCommand(queryParams)); + res.json(data.Items); + } catch (err) { + res.statusCode = 500; + res.json({error: 'Could not load items: ' + err.message}); + } +}); + +/***************************************** + * HTTP Get method for get single object * + *****************************************/ + +app.get(path + '/object' + hashKeyPath + sortKeyPath, async function(req, res) { + const params = {}; + if (userIdPresent && req.apiGateway) { + params[partitionKeyName] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH; + } else { + params[partitionKeyName] = req.params[partitionKeyName]; + try { + params[partitionKeyName] = convertUrlType(req.params[partitionKeyName], partitionKeyType); + } catch(err) { + res.statusCode = 500; + res.json({error: 'Wrong column type ' + err}); + } + } + if (hasSortKey) { + try { + params[sortKeyName] = convertUrlType(req.params[sortKeyName], sortKeyType); + } catch(err) { + res.statusCode = 500; + res.json({error: 'Wrong column type ' + err}); + } + } + + let getItemParams = { + TableName: tableName, + Key: params + } + + try { + const data = await ddbDocClient.send(new GetCommand(getItemParams)); + if (data.Item) { + res.json(data.Item); + } else { + res.json(data) ; + } + } catch (err) { + res.statusCode = 500; + res.json({error: 'Could not load items: ' + err.message}); + } +}); + + +/************************************ +* HTTP put method for insert object * +*************************************/ + +app.put(path, async function(req, res) { + + if (userIdPresent) { + req.body['userId'] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH; + } + + let putItemParams = { + TableName: tableName, + Item: req.body + } + try { + let data = await ddbDocClient.send(new PutCommand(putItemParams)); + res.json({ success: 'put call succeed!', url: req.url, data: data }) + } catch (err) { + res.statusCode = 500; + res.json({ error: err, url: req.url, body: req.body }); + } +}); + +/************************************ +* HTTP post method for insert object * +*************************************/ + +app.post(path, async function(req, res) { + + if (userIdPresent) { + req.body['userId'] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH; + } + + let putItemParams = { + TableName: tableName, + Item: req.body + } + try { + let data = await ddbDocClient.send(new PutCommand(putItemParams)); + res.json({ success: 'post call succeed!', url: req.url, data: data }) + } catch (err) { + res.statusCode = 500; + res.json({ error: err, url: req.url, body: req.body }); + } +}); + +/************************************** +* HTTP remove method to delete object * +***************************************/ + +app.delete(path + '/object' + hashKeyPath + sortKeyPath, async function(req, res) { + const params = {}; + if (userIdPresent && req.apiGateway) { + params[partitionKeyName] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH; + } else { + params[partitionKeyName] = req.params[partitionKeyName]; + try { + params[partitionKeyName] = convertUrlType(req.params[partitionKeyName], partitionKeyType); + } catch(err) { + res.statusCode = 500; + res.json({error: 'Wrong column type ' + err}); + } + } + if (hasSortKey) { + try { + params[sortKeyName] = convertUrlType(req.params[sortKeyName], sortKeyType); + } catch(err) { + res.statusCode = 500; + res.json({error: 'Wrong column type ' + err}); + } + } + + let removeItemParams = { + TableName: tableName, + Key: params + } + + try { + let data = await ddbDocClient.send(new DeleteCommand(removeItemParams)); + res.json({url: req.url, data: data}); + } catch (err) { + res.statusCode = 500; + res.json({error: err, url: req.url}); + } +}); + +app.listen(3000, function() { + console.log("App started") +}); + +// Export the app object. When executing the application local this does nothing. However, +// to port it to AWS Lambda we will create a wrapper around that will load the app from +// this file +module.exports = app diff --git a/amplify/backend/function/ab3examapi/src/event.json b/amplify/backend/function/ab3examapi/src/event.json new file mode 100644 index 00000000..77c9a116 --- /dev/null +++ b/amplify/backend/function/ab3examapi/src/event.json @@ -0,0 +1,11 @@ +{ + "httpMethod": "POST", + "path": "/item", + "queryStringParameters": { + "limit": "10" + }, + "headers": { + "Content-Type": "application/json" + }, + "body": "{\"msg\":\"Hello from the event.json body\"}" +} diff --git a/amplify/backend/function/ab3examapi/src/index.js b/amplify/backend/function/ab3examapi/src/index.js new file mode 100644 index 00000000..832a7f6e --- /dev/null +++ b/amplify/backend/function/ab3examapi/src/index.js @@ -0,0 +1,15 @@ +const awsServerlessExpress = require('aws-serverless-express'); +const app = require('./app'); + +/** + * @type {import('http').Server} + */ +const server = awsServerlessExpress.createServer(app); + +/** + * @type {import('@types/aws-lambda').APIGatewayProxyHandler} + */ +exports.handler = (event, context) => { + console.log(`EVENT: ${JSON.stringify(event)}`); + return awsServerlessExpress.proxy(server, event, context, 'PROMISE').promise; +}; diff --git a/amplify/backend/function/ab3examapi/src/package.json b/amplify/backend/function/ab3examapi/src/package.json new file mode 100644 index 00000000..a96a7097 --- /dev/null +++ b/amplify/backend/function/ab3examapi/src/package.json @@ -0,0 +1,19 @@ +{ + "name": "ab3examapi", + "version": "1.0.0", + "description": "Lambda function generated by Amplify", + "main": "index.js", + "license": "Apache-2.0", + "dependencies": { + "aws-serverless-express": "^3.3.5", + "body-parser": "^1.19.1", + "express": "^4.17.2" + }, + "devDependencies": { + "@types/aws-lambda": "^8.10.92", + "@aws-sdk/client-dynamodb": "^3.188.0", + "@aws-sdk/lib-dynamodb": "^3.188.0", + "@aws-sdk/smithy-client": "^3.188.0", + "@aws-sdk/types" : "^3.188.0" + } +} diff --git a/amplify/backend/storage/ab3dynamodb/cli-inputs.json b/amplify/backend/storage/ab3dynamodb/cli-inputs.json new file mode 100644 index 00000000..ceb7c82b --- /dev/null +++ b/amplify/backend/storage/ab3dynamodb/cli-inputs.json @@ -0,0 +1,14 @@ +{ + "resourceName": "ab3dynamodb", + "tableName": "ab3examtable", + "partitionKey": { + "fieldName": "id", + "fieldType": "number" + }, + "sortKey": { + "fieldName": "subject", + "fieldType": "string" + }, + "gsi": [], + "triggerFunctions": [] +} \ No newline at end of file diff --git a/src/assets/AElogo.svg b/src/assets/AElogo.svg new file mode 100644 index 00000000..07807dd3 --- /dev/null +++ b/src/assets/AElogo.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/views/exam/Exam.vue b/src/views/exam/Exam.vue new file mode 100644 index 00000000..1da6cc5e --- /dev/null +++ b/src/views/exam/Exam.vue @@ -0,0 +1,81 @@ + + + \ No newline at end of file diff --git a/src/views/exam/Result.vue b/src/views/exam/Result.vue new file mode 100644 index 00000000..ea87fde2 --- /dev/null +++ b/src/views/exam/Result.vue @@ -0,0 +1,95 @@ + + + + \ No newline at end of file diff --git a/src/views/exam/ResultList.vue b/src/views/exam/ResultList.vue new file mode 100644 index 00000000..1da6cc5e --- /dev/null +++ b/src/views/exam/ResultList.vue @@ -0,0 +1,81 @@ + + + \ No newline at end of file diff --git a/src/views/exam/Submission.vue b/src/views/exam/Submission.vue new file mode 100644 index 00000000..4003cc7b --- /dev/null +++ b/src/views/exam/Submission.vue @@ -0,0 +1,108 @@ + + + + + + \ No newline at end of file diff --git a/src/views/exam/students.js b/src/views/exam/students.js new file mode 100644 index 00000000..d55cf1a1 --- /dev/null +++ b/src/views/exam/students.js @@ -0,0 +1,18 @@ +export const students = [ + { id: 1, name: 'AK', class: 'Class A' }, + { id: 2, name: 'Abdullah', class: 'Class B' }, + { id: 3, name: 'Bob Johnson', class: 'Class A' }, + { id: 4, name: 'Alice Williams', class: 'Class A' }, + { id: 5, name: 'Tom Davis', class: 'Class A' }, + { id: 6, name: 'Sarah Lee', class: 'Class A' }, + { id: 7, name: 'Michael Chen', class: 'Class B' }, + { id: 8, name: 'Emily Patel', class: 'Class B' }, + { id: 9, name: 'David Nguyen', class: 'Class B' }, + { id: 10, name: 'Olivia Gonzalez', class: 'Class B' }, + { id: 11, name: 'Ryan Kim', class: 'Class C' }, + { id: 12, name: 'Sophia Tanaka', class: 'Class C' }, + { id: 11, name: 'Ryan Kim', class: 'Class C' }, + { id: 12, name: 'Sophia Tanaka', class: 'Class C' }, + { id: 12, name: 'Sophia Tanaka', class: 'Class C' } + ]; + \ No newline at end of file From e38bed4ba3ef9bb6896dc270d6914c7134d741c3 Mon Sep 17 00:00:00 2001 From: khairikz Date: Thu, 9 May 2024 09:27:18 +0800 Subject: [PATCH 4/6] update UI --- src/_nav.js | 462 ++++++++++++---------- src/assets/icons/index.js | 2 + src/components/AppFooter.vue | 4 +- src/components/AppHeaderDropdownAccnt.vue | 13 +- src/views/dashboard/Dashboard.vue | 6 +- src/views/exam/Result.vue | 38 +- src/views/exam/ResultList.vue | 13 +- src/views/widgets/WidgetsStatsTypeA.vue | 8 +- 8 files changed, 302 insertions(+), 244 deletions(-) diff --git a/src/_nav.js b/src/_nav.js index c76bef52..eab7b6e8 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -33,245 +33,273 @@ export default [ }, { component: 'CNavTitle', - name: 'Theme', + name: 'Finance', }, { component: 'CNavItem', - name: 'Colors', + name: 'Payment', to: '/theme/colors', - icon: 'cil-drop', + icon: 'cil-calculator', }, { component: 'CNavItem', - name: 'Typography', + name: 'History', to: '/theme/typography', - icon: 'cil-pencil', + icon: 'cil-history', + }, + { + component: 'CNavItem', + name: 'Receipt', + to: '/theme/typography', + icon: 'cil-drop', }, { component: 'CNavTitle', - name: 'Components', + name: 'Registration', }, { - component: 'CNavGroup', - name: 'Base', - to: '/base', + component: 'CNavItem', + name: 'Subject', + to: '/theme/colors', icon: 'cil-puzzle', - items: [ - { - component: 'CNavItem', - name: 'Accordion', - to: '/base/accordion', - }, - { - component: 'CNavItem', - name: 'Breadcrumbs', - to: '/base/breadcrumbs', - }, - { - component: 'CNavItem', - name: 'Cards', - to: '/base/cards', - }, - { - component: 'CNavItem', - name: 'Carousels', - to: '/base/carousels', - }, - { - component: 'CNavItem', - name: 'Collapses', - to: '/base/collapses', - }, - { - component: 'CNavItem', - name: 'List Groups', - to: '/base/list-groups', - }, - { - component: 'CNavItem', - name: 'Navs & Tabs', - to: '/base/navs', - }, - { - component: 'CNavItem', - name: 'Paginations', - to: '/base/paginations', - }, - { - component: 'CNavItem', - name: 'Placeholders', - to: '/base/placeholders', - }, - { - component: 'CNavItem', - name: 'Popovers', - to: '/base/popovers', - }, - { - component: 'CNavItem', - name: 'Progress', - to: '/base/progress', - }, - { - component: 'CNavItem', - name: 'Spinners', - to: '/base/spinners', - }, - { - component: 'CNavItem', - name: 'Tables', - to: '/base/tables', - }, - { - component: 'CNavItem', - name: 'Tooltips', - to: '/base/tooltips', - }, - ], }, { - component: 'CNavGroup', - name: 'Buttons', - to: '/buttons', + component: 'CNavItem', + name: 'Class', + to: '/theme/typography', icon: 'cil-cursor', - items: [ - { - component: 'CNavItem', - name: 'Buttons', - to: '/buttons/standard-buttons', - }, - { - component: 'CNavItem', - name: 'Button Groups', - to: '/buttons/button-groups', - }, - { - component: 'CNavItem', - name: 'Dropdowns', - to: '/buttons/dropdowns', - }, - ], - }, - { - component: 'CNavGroup', - name: 'Forms', - to: '/forms', - icon: 'cil-notes', - items: [ - { - component: 'CNavItem', - name: 'Form Control', - to: '/forms/form-control', - }, - { - component: 'CNavItem', - name: 'Select', - to: '/forms/select', - }, - { - component: 'CNavItem', - name: 'Checks & Radios', - to: '/forms/checks-radios', - }, - { - component: 'CNavItem', - name: 'Range', - to: '/forms/range', - }, - { - component: 'CNavItem', - name: 'Input Group', - to: '/forms/input-group', - }, - { - component: 'CNavItem', - name: 'Floating Labels', - to: '/forms/floating-labels', - }, - { - component: 'CNavItem', - name: 'Layout', - to: '/forms/layout', - }, - { - component: 'CNavItem', - name: 'Validation', - to: '/forms/validation', - }, - ], }, { component: 'CNavItem', - name: 'Charts', - to: '/charts', + name: 'Calendar', + to: '/theme/typography', icon: 'cil-chart-pie', }, - { - component: 'CNavGroup', - name: 'Icons', - to: '/icons', - icon: 'cil-star', - items: [ - { - component: 'CNavItem', - name: 'CoreUI Icons', - to: '/icons/coreui-icons', - badge: { - color: 'info', - text: 'NEW', - }, - }, - { - component: 'CNavItem', - name: 'Brands', - to: '/icons/brands', - }, - { - component: 'CNavItem', - name: 'Flags', - to: '/icons/flags', - }, - ], - }, - { - component: 'CNavGroup', - name: 'Notifications', - to: '/notifications', - icon: 'cil-bell', - items: [ - { - component: 'CNavItem', - name: 'Alerts', - to: '/notifications/alerts', - }, - { - component: 'CNavItem', - name: 'Badges', - to: '/notifications/badges', - }, - { - component: 'CNavItem', - name: 'Modals', - to: '/notifications/modals', - }, - { - component: 'CNavItem', - name: 'Toasts', - to: '/notifications/toasts', - }, - ], - }, - { - component: 'CNavItem', - name: 'Widgets', - to: '/widgets', - icon: 'cil-calculator', - badge: { - color: 'primary', - text: 'NEW', - shape: 'pill', - }, - }, + // { + // component: 'CNavTitle', + // name: 'Components', + // }, + // { + // component: 'CNavGroup', + // name: 'Base', + // to: '/base', + // icon: 'cil-puzzle', + // items: [ + // { + // component: 'CNavItem', + // name: 'Accordion', + // to: '/base/accordion', + // }, + // { + // component: 'CNavItem', + // name: 'Breadcrumbs', + // to: '/base/breadcrumbs', + // }, + // { + // component: 'CNavItem', + // name: 'Cards', + // to: '/base/cards', + // }, + // { + // component: 'CNavItem', + // name: 'Carousels', + // to: '/base/carousels', + // }, + // { + // component: 'CNavItem', + // name: 'Collapses', + // to: '/base/collapses', + // }, + // { + // component: 'CNavItem', + // name: 'List Groups', + // to: '/base/list-groups', + // }, + // { + // component: 'CNavItem', + // name: 'Navs & Tabs', + // to: '/base/navs', + // }, + // { + // component: 'CNavItem', + // name: 'Paginations', + // to: '/base/paginations', + // }, + // { + // component: 'CNavItem', + // name: 'Placeholders', + // to: '/base/placeholders', + // }, + // { + // component: 'CNavItem', + // name: 'Popovers', + // to: '/base/popovers', + // }, + // { + // component: 'CNavItem', + // name: 'Progress', + // to: '/base/progress', + // }, + // { + // component: 'CNavItem', + // name: 'Spinners', + // to: '/base/spinners', + // }, + // { + // component: 'CNavItem', + // name: 'Tables', + // to: '/base/tables', + // }, + // { + // component: 'CNavItem', + // name: 'Tooltips', + // to: '/base/tooltips', + // }, + // ], + // }, + // { + // component: 'CNavGroup', + // name: 'Buttons', + // to: '/buttons', + // icon: 'cil-cursor', + // items: [ + // { + // component: 'CNavItem', + // name: 'Buttons', + // to: '/buttons/standard-buttons', + // }, + // { + // component: 'CNavItem', + // name: 'Button Groups', + // to: '/buttons/button-groups', + // }, + // { + // component: 'CNavItem', + // name: 'Dropdowns', + // to: '/buttons/dropdowns', + // }, + // ], + // }, + // { + // component: 'CNavGroup', + // name: 'Forms', + // to: '/forms', + // icon: 'cil-notes', + // items: [ + // { + // component: 'CNavItem', + // name: 'Form Control', + // to: '/forms/form-control', + // }, + // { + // component: 'CNavItem', + // name: 'Select', + // to: '/forms/select', + // }, + // { + // component: 'CNavItem', + // name: 'Checks & Radios', + // to: '/forms/checks-radios', + // }, + // { + // component: 'CNavItem', + // name: 'Range', + // to: '/forms/range', + // }, + // { + // component: 'CNavItem', + // name: 'Input Group', + // to: '/forms/input-group', + // }, + // { + // component: 'CNavItem', + // name: 'Floating Labels', + // to: '/forms/floating-labels', + // }, + // { + // component: 'CNavItem', + // name: 'Layout', + // to: '/forms/layout', + // }, + // { + // component: 'CNavItem', + // name: 'Validation', + // to: '/forms/validation', + // }, + // ], + // }, + // { + // component: 'CNavItem', + // name: 'Charts', + // to: '/charts', + // icon: 'cil-chart-pie', + // }, + // { + // component: 'CNavGroup', + // name: 'Icons', + // to: '/icons', + // icon: 'cil-star', + // items: [ + // { + // component: 'CNavItem', + // name: 'CoreUI Icons', + // to: '/icons/coreui-icons', + // badge: { + // color: 'info', + // text: 'NEW', + // }, + // }, + // { + // component: 'CNavItem', + // name: 'Brands', + // to: '/icons/brands', + // }, + // { + // component: 'CNavItem', + // name: 'Flags', + // to: '/icons/flags', + // }, + // ], + // }, + // { + // component: 'CNavGroup', + // name: 'Notifications', + // to: '/notifications', + // icon: 'cil-bell', + // items: [ + // { + // component: 'CNavItem', + // name: 'Alerts', + // to: '/notifications/alerts', + // }, + // { + // component: 'CNavItem', + // name: 'Badges', + // to: '/notifications/badges', + // }, + // { + // component: 'CNavItem', + // name: 'Modals', + // to: '/notifications/modals', + // }, + // { + // component: 'CNavItem', + // name: 'Toasts', + // to: '/notifications/toasts', + // }, + // ], + // }, + // { + // component: 'CNavItem', + // name: 'Widgets', + // to: '/widgets', + // icon: 'cil-calculator', + // badge: { + // color: 'primary', + // text: 'NEW', + // shape: 'pill', + // }, + // }, { component: 'CNavTitle', name: 'Extras', diff --git a/src/assets/icons/index.js b/src/assets/icons/index.js index 69d9ceae..5ea61ddf 100644 --- a/src/assets/icons/index.js +++ b/src/assets/icons/index.js @@ -68,6 +68,7 @@ import { cilPencil, cilPeople, cilPuzzle, + cilHistory, cilSchool, cilSettings, cilShieldAlt, @@ -127,6 +128,7 @@ export const iconsSet = Object.assign( cilPencil, cilPeople, cilPuzzle, + cilHistory, cilSchool, cilSettings, cilShieldAlt, diff --git a/src/components/AppFooter.vue b/src/components/AppFooter.vue index b62df395..6c22eaf7 100644 --- a/src/components/AppFooter.vue +++ b/src/components/AppFooter.vue @@ -1,9 +1,9 @@ @@ -19,10 +22,10 @@ export default { classFilter: '', subjectFilter: '', columns: [ - { - key: 'id', - _props: { scope: 'col' }, - }, + // { + // key: 'id', + // _props: { scope: 'col' }, + // }, { key: 'name', _props: { scope: 'col' }, diff --git a/src/views/widgets/WidgetsStatsTypeA.vue b/src/views/widgets/WidgetsStatsTypeA.vue index e914b7f2..2a6c1b6e 100644 --- a/src/views/widgets/WidgetsStatsTypeA.vue +++ b/src/views/widgets/WidgetsStatsTypeA.vue @@ -29,7 +29,7 @@ onMounted(() => { >26K (-12.4% ) - + - + - + - +