diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 00000000..737669c6 --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "chinese-newcomers-dev" + } +} diff --git a/firebase.json b/firebase.json new file mode 100644 index 00000000..812bb8d9 --- /dev/null +++ b/firebase.json @@ -0,0 +1,27 @@ +{ + "functions": [ + { + "source": "functions", + "codebase": "default", + "ignore": [ + "node_modules", + ".git", + "firebase-debug.log", + "firebase-debug.*.log" + ], + "predeploy": ["npm --prefix \"$RESOURCE_DIR\" run build"] + } + ], + "emulators": { + "functions": { + "port": 5001 + }, + "ui": { + "enabled": true + }, + "singleProjectMode": true, + "pubsub": { + "port": 8085 + } + } +} diff --git a/functions/.gitignore b/functions/.gitignore new file mode 100644 index 00000000..65b4c06e --- /dev/null +++ b/functions/.gitignore @@ -0,0 +1,9 @@ +# Compiled JavaScript files +lib/**/*.js +lib/**/*.js.map + +# TypeScript v1 declaration files +typings/ + +# Node.js dependency directory +node_modules/ diff --git a/functions/package.json b/functions/package.json new file mode 100644 index 00000000..38d8bce0 --- /dev/null +++ b/functions/package.json @@ -0,0 +1,27 @@ +{ + "name": "functions", + "scripts": { + "build": "tsc", + "build:watch": "tsc --watch", + "serve": "npm run build && firebase emulators:start --only functions", + "shell": "npm run build && firebase functions:shell", + "start": "npm run shell", + "deploy": "firebase deploy --only functions", + "logs": "firebase functions:log" + }, + "engines": { + "node": "16" + }, + "main": "lib/index.js", + "dependencies": { + "firebase-admin": "^11.5.0", + "firebase-functions": "^4.2.0", + "json2csv": "^6.0.0-alpha.2" + }, + "devDependencies": { + "@types/json2csv": "^5.0.3", + "firebase-functions-test": "^3.0.0", + "typescript": "^4.9.0" + }, + "private": true +} diff --git a/functions/pubsub-debug.log b/functions/pubsub-debug.log new file mode 100644 index 00000000..2a335a85 --- /dev/null +++ b/functions/pubsub-debug.log @@ -0,0 +1,11 @@ +This is the Google Pub/Sub fake. +Implementation may be incomplete or differ from the real system. +Apr 29, 2023 3:46:16 PM com.google.cloud.pubsub.testing.v1.Main main +INFO: IAM integration is disabled. IAM policy methods and ACL checks are not supported +SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". +SLF4J: Defaulting to no-operation (NOP) logger implementation +SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. +Apr 29, 2023 3:46:16 PM com.google.cloud.pubsub.testing.v1.Main main +INFO: Server started, listening on 8085 +Apr 29, 2023 3:46:17 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead +INFO: Detected HTTP/2 connection. diff --git a/functions/src/index.ts b/functions/src/index.ts new file mode 100644 index 00000000..9ee42bcd --- /dev/null +++ b/functions/src/index.ts @@ -0,0 +1,121 @@ +import functions = require('firebase-functions'); +import admin = require('firebase-admin'); +import json2csv = require('json2csv'); + +admin.initializeApp(); +const firestoreAdmin = admin.firestore(); + +exports.updateExpiredJobs = functions.pubsub + .schedule('0 12 * * *') + .onRun(async () => { + const approvedJobsCollection = firestoreAdmin.collection('approvedJobs'); + const approvedJobs = approvedJobsCollection.get(); + const now = new Date(); + (await approvedJobs).forEach(async job => { + const jobId = job.get('id'); + const jobDate = new Date(job.get('date').seconds * 1000); + const diff = now.getTime() - jobDate.getTime(); + if (diff > 5184000000) { + const expiredJobsCollection = firestoreAdmin.collection('expiredJobs'); + expiredJobsCollection.doc(jobId).set(job.data()); + job.ref.delete(); + } + }); + }); + +exports.createCSV = functions.pubsub + .schedule('0 12 * * *') // change to 30 days + .onRun(async () => { + const approvedJobsCollection = firestoreAdmin.collection('expiredJobs'); + const approvedJobs = approvedJobsCollection.get(); + const jobs: admin.firestore.DocumentData[] = []; + (await approvedJobs).forEach(async job => { + jobs.push(job.data()); + }); + // TODO: export jobs to google sheet + + // const csv = await json2csv.parse({ data: jobs }); + // firestoreAdmin.collection('mail').add({ + // to: ['stephwonggg23@gmail.com'], + // message: { + // subject: 'Expired Jobs', + // msg: 'Here are the new expired jobs from the job feed.', + // attachements: csv, + // }, + // }); + // delete expired jobs + const expiredJobsCollection = firestoreAdmin.collection('expiredJobs'); + const snapshot = await expiredJobsCollection.get(); + const batch = firestoreAdmin.batch(); + snapshot.docs.forEach(doc => { + batch.delete(doc.ref); + }); + await batch.commit(); + }); + +// let databaseName = ''; +// exports.scheduledFirestoreExport = functions.pubsub +// .schedule('0 12 * * *') +// .onRun(async () => { +// const projectId = process.env.GCP_PROJECT || process.env.GCLOUD_PROJECT; +// if (projectId) { +// databaseName = client.databasePath(projectId, '(default)'); +// } +// // maybe check to make sure expiredJobs isn't empty first to not send blank document +// client.exportDocuments({ +// name: databaseName, +// outputUriPrefix: bucket, +// // Leave collectionIds empty to export all collections +// // or set to a list of collection IDs to export, +// // collectionIds: ['users', 'posts'] +// collectionIds: ['expiredJobs'], +// }); +// // send email -- how will we know which csv file to get from backing +// // delete bucket? +// // what is the file name --> maybe use getFiles bc there should only be one file if we delete right away +// admin +// .storage() +// .bucket('expired-jobs') +// .getFiles() +// .then(files => { +// functions.logger.log(files); +// // console.log(files); +// }); +// admin.storage().bucket('expired-jobs'). +// admin.storage().bucket('expired-jobs').file('test').delete(); +// admin.storage().bucket().file().delete() // pass in bucket name into bucket and fileame into file +// delete from expired jobs + +// return client +// .exportDocuments({ +// name: databaseName, +// outputUriPrefix: bucket, +// // Leave collectionIds empty to export all collections +// // or set to a list of collection IDs to export, +// // collectionIds: ['users', 'posts'] +// collectionIds: ['expiredJobs'], +// }) +// .then(responses => { +// const response = responses[0]; +// functions.logger.log(`Operation Name: ${response.name}`); +// console.log(`Operation Name: ${response.name}`); +// }) +// .catch(err => { +// console.error(err); +// throw new Error('Export operation failed'); +// }); +// }); + +// exports.sendEmail = functions.pubsub.schedule('0 12 * * *').onRun(async () => { +// functions.logger.log('send email function'); +// firestoreAdmin.collection('mail').add({ +// to: ['stephwonggg23@gmail.com'], +// message: { +// subject: 'Expired Jobs', +// msg: 'Here are the new expired jobs from the job feed.', +// attachements: firestoreAdmin.collection('expiredJobs'), +// // add expired job +// }, +// }); +// // remove expired jobs +// }); diff --git a/functions/tsconfig.json b/functions/tsconfig.json new file mode 100644 index 00000000..eab6749f --- /dev/null +++ b/functions/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noImplicitReturns": true, + "noUnusedLocals": true, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "skipLibCheck": true + }, + "compileOnSave": true, + "include": ["src"] +} diff --git a/functions/ui-debug.log b/functions/ui-debug.log new file mode 100644 index 00000000..edaaf3e0 --- /dev/null +++ b/functions/ui-debug.log @@ -0,0 +1,2 @@ +Web / API server started at 127.0.0.1:4000 +Web / API server started at ::1:4000 diff --git a/package.json b/package.json index 0bc5bf5c..c94aefd4 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "expo-font": "~10.2.0", "expo-status-bar": "~1.4.0", "firebase": "9.12.0", + "firebase-admin": "^11.5.0", + "firebase-functions": "^4.3.0", "global": "^4.4.0", "i18next": "^22.0.6", "idb": "^7.0.2", diff --git a/pubsub-debug.log b/pubsub-debug.log new file mode 100644 index 00000000..d3dac060 --- /dev/null +++ b/pubsub-debug.log @@ -0,0 +1,11 @@ +This is the Google Pub/Sub fake. +Implementation may be incomplete or differ from the real system. +Apr 16, 2023 2:49:03 PM com.google.cloud.pubsub.testing.v1.Main main +INFO: IAM integration is disabled. IAM policy methods and ACL checks are not supported +SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". +SLF4J: Defaulting to no-operation (NOP) logger implementation +SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. +Apr 16, 2023 2:49:03 PM com.google.cloud.pubsub.testing.v1.Main main +INFO: Server started, listening on 8085 +Apr 16, 2023 2:49:05 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead +INFO: Detected HTTP/2 connection. diff --git a/src/firebase/firestore/job.ts b/src/firebase/firestore/job.ts index e416415d..8d3f85f8 100644 --- a/src/firebase/firestore/job.ts +++ b/src/firebase/firestore/job.ts @@ -64,7 +64,9 @@ export const getMonthlyCounter = async (): Promise => { function parseFirestoreListenerJob(jobId: string, job: Partial) { const jobKeys = Object.keys(jobInstance); - const parsedJob = Object.fromEntries(jobKeys.map(k => [k, job[k as keyof typeof job]])); + const parsedJob = Object.fromEntries( + jobKeys.map(k => [k, job[k as keyof typeof job]]), + ); parsedJob.id = jobId; return parsedJob; } diff --git a/src/screens/Drafting/Draft.tsx b/src/screens/Drafting/Draft.tsx index 65ad6472..a2c0f566 100644 --- a/src/screens/Drafting/Draft.tsx +++ b/src/screens/Drafting/Draft.tsx @@ -39,7 +39,7 @@ function DraftScreen({ 'other', ]; - const [dateIsEnabled, setDateIsEnabled] = React.useState(true); + // const [dateIsEnabled, setDateIsEnabled] = React.useState(true); const [companyNameIsEnabled, setCompanyNameIsEnabled] = React.useState(true); const [addressIsEnabled, setAddressIsEnabled] = React.useState(true); const [contactPersonIsEnabled, setContactPersonIsEnabled] = @@ -61,7 +61,7 @@ function DraftScreen({ const [modalJobText, setModalJobText] = React.useState(''); interface FormValues { - date: string; + // date: Date; companyName: string; address: string; contactPerson: string; diff --git a/ui-debug.log b/ui-debug.log new file mode 100644 index 00000000..edaaf3e0 --- /dev/null +++ b/ui-debug.log @@ -0,0 +1,2 @@ +Web / API server started at 127.0.0.1:4000 +Web / API server started at ::1:4000