diff --git a/backend/src/graphql/resolvers/analytics.resolver.ts b/backend/src/graphql/resolvers/analytics.resolver.ts index 381659d..2a8af6e 100644 --- a/backend/src/graphql/resolvers/analytics.resolver.ts +++ b/backend/src/graphql/resolvers/analytics.resolver.ts @@ -163,6 +163,35 @@ const analyticsResolver = { } }, + getScholarApplyClicksRankedWithDateRange: async (_: any, { startDate, endDate }: any, { dataSources }: any) => { + const { db } = dataSources + const client = await establishConnection(db) + try { + const query = ` + SELECT scholar.scholar_id, scholar.name, scholar.email, COUNT(*) AS clicks + FROM apply_clicks + JOIN scholar ON apply_clicks.scholar_id = scholar.scholar_id + WHERE apply_clicks.click_time BETWEEN $1 AND $2 + GROUP BY scholar.scholar_id, scholar.name, scholar.email + ORDER BY clicks DESC + ` + const resp = await client.query(query, [startDate, endDate]) + console.log(resp.rows) + const formattedRows = resp.rows.map((row: any) => ({ + scholarId: row.scholar_id, + apply_count: parseInt(row.clicks), + scholarName: row.name, + scholarEmail: row.email, + })) + return formattedRows + } catch (err) { + console.error(err) + throw new Error('Failed to fetch scholar job clicks with date range') + } finally { + client.release() + } + }, + getScholarJobClicksRanked: async ( _: any, args: any, @@ -195,6 +224,35 @@ const analyticsResolver = { } }, + getScholarJobClicksRankedWithDateRange: async (_: any, { startDate, endDate }: any, { dataSources }: any) => { + const { db } = dataSources + const client = await establishConnection(db) + try { + const query = ` + SELECT scholar.scholar_id, scholar.name, scholar.email, COUNT(*) AS clicks + FROM job_clicks + JOIN scholar ON job_clicks.scholar_id = scholar.scholar_id + WHERE job_clicks.click_time BETWEEN $1 AND $2 + GROUP BY scholar.scholar_id, scholar.name, scholar.email + ORDER BY clicks DESC + ` + const resp = await client.query(query, [startDate, endDate]) + console.log(resp.rows) + const formattedRows = resp.rows.map((row: any) => ({ + scholarId: row.scholar_id, + job_count: parseInt(row.clicks), + scholarName: row.name, + scholarEmail: row.email, + })) + return formattedRows + } catch (err) { + console.error(err) + throw new Error('Failed to fetch scholar job clicks with date range') + } finally { + client.release() + } + }, + getScholarClicksBySchool: async ( _: any, args: any, @@ -224,6 +282,32 @@ const analyticsResolver = { } }, + getScholarClicksBySchoolWithDateRange: async (_: any, { startDate, endDate }: any, { dataSources }: any) => { + const { db } = dataSources + const client = await establishConnection(db) + try { + const query = ` + SELECT school, COUNT(*) AS scholar_click_count + FROM job_clicks JOIN scholar ON job_clicks.scholar_id = scholar.scholar_id + WHERE job_clicks.click_time BETWEEN $1 AND $2 + GROUP BY school + ORDER BY scholar_click_count DESC + ` + const resp = await client.query(query, [startDate, endDate]) + console.log(resp.rows) + const formattedRows = resp.rows.map((row: any) => ({ + school: row.school, + scholar_click_count: parseInt(row.scholar_click_count), + })) + return formattedRows + } catch (err) { + console.error('Error executing query:', err) + throw new Error('Failed to get scholar clicks by school with date range') + } finally { + client.release() + } + }, + getScholarEmployerClicksRanked: async ( _: any, args: any, @@ -256,6 +340,35 @@ const analyticsResolver = { } }, + getScholarEmployerClicksRankedWithDateRange: async (_: any, { startDate, endDate }: any, { dataSources }: any) => { + const { db } = dataSources + const client = await establishConnection(db) + try { + const query = ` + SELECT scholar.scholar_id, scholar.name, scholar.email, COUNT(*) AS clicks + FROM employer_clicks + JOIN scholar ON employer_clicks.scholar_id = scholar.scholar_id + WHERE employer_clicks.click_time BETWEEN $1 AND $2 + GROUP BY scholar.scholar_id, scholar.name, scholar.email + ORDER BY clicks DESC + ` + const resp = await client.query(query, [startDate, endDate]) + console.log(resp.rows) + const formattedRows = resp.rows.map((row: any) => ({ + scholarId: row.scholar_id, + employer_count: parseInt(row.clicks), + scholarName: row.name, + scholarEmail: row.email, + })) + return formattedRows + } catch (err) { + console.error(err) + throw new Error('Failed to fetch scholar employer clicks with date range') + } finally { + client.release() + } + }, + getJobClicks: async (_: any, args: any, { dataSources }: any) => { const { db } = dataSources; const client = await establishConnection(db); @@ -435,6 +548,33 @@ const analyticsResolver = { client.release(); } }, + + getJobTagRankingByClicksWithDateRange: async (_: any, { startDate, endDate }: any, { dataSources }: any) => { + const { db } = dataSources + const client = await establishConnection(db) + try { + const query = ` + SELECT unnest(tags) AS tag, COUNT(*) AS click_count + FROM job_clicks + JOIN job ON job_clicks.job_id = job.job_id + WHERE tags IS NOT NULL and click_time BETWEEN $1 AND $2 + GROUP BY tag + ORDER BY click_count DESC + ` + const resp = await client.query(query, [startDate, endDate]) + const formattedRows = resp.rows.map((row: any) => ({ + tag: row.tag, + click_count: parseInt(row.click_count), + })) + return formattedRows + } catch (err) { + console.error('Error executing query:', err) + throw new Error('Failed to get job tag ranking by clicks with date range') + } finally { + client.release() + } + }, + getApplyClicksForScholar: async ( _: any, { scholarId }: any, @@ -706,6 +846,33 @@ const analyticsResolver = { } }, + getEmployerJobPostingsRankingWithDateRange: async (_: any, { startDate, endDate }: any, { dataSources }: any) => { + const { db } = dataSources + const client = await establishConnection(db) + try { + const query = ` + SELECT employer.name, employer.employer_id, COUNT(*) AS job_click_count + FROM job JOIN job_clicks ON job.job_id = job_clicks.job_id JOIN employer ON job.employer_id = employer.employer_id + WHERE live = true AND job_clicks.click_time BETWEEN $1 AND $2 + GROUP BY employer.employer_id + ORDER BY job_click_count DESC + ` + const resp = await client.query(query, [startDate, endDate]) + console.log(resp.rows) + const formattedRows = resp.rows.map((row: any) => ({ + employerName: row.name, + employerId: row.employer_id, + job_posting_click_count: parseInt(row.job_click_count), + })) + return formattedRows + } catch (err) { + console.error('Error executing query:', err) + throw new Error('Failed to get employer job postings ranking with date range') + } finally { + client.release() + } + }, + getNumDaysSinceLastJobPostByEmployer: async ( _: any, args: any, @@ -1077,6 +1244,8 @@ const analyticsResolver = { } catch (err) { console.error("Error executing query:", err); throw new Error("Failed to get custom analytics"); + } finally { + client.release(); } }, }, diff --git a/backend/src/graphql/typeDefs/analytics.typedef.ts b/backend/src/graphql/typeDefs/analytics.typedef.ts index a0ad49d..04ac308 100644 --- a/backend/src/graphql/typeDefs/analytics.typedef.ts +++ b/backend/src/graphql/typeDefs/analytics.typedef.ts @@ -1,8 +1,7 @@ -import {gql} from "apollo-server-lambda"; +import { gql } from 'apollo-server-lambda' export const analyticsTypeDefs = gql` - - type Query { + type Query { getJobClicks: [JobClick] getApplyClicks: [ApplyClicks] getEmployerClicks: [EmployerClick] @@ -10,17 +9,23 @@ export const analyticsTypeDefs = gql` getEmployerClicksRanked: [RankedEmployerClick] getJobTagRanking: [JobTagRanking] getJobTagRankingByClicks: [JobTagRankingByClick] + getJobTagRankingByClicksWithDateRange(startDate: Date, endDate: Date): [JobTagRankingByClick] getJobLocationRanking: [JobLocationRanking] getJobDeadlineRankingByMonth: [JobDeadlineRanking] getScholarsRankedByMajor: [MajorRanking] getScholarsRankedByYear: [YearRanking] getPercentageOfScholarsWithAllowedNotifications: Int getScholarApplyClicksRanked: [ApplyClickRank] + getScholarApplyClicksRankedWithDateRange(startDate: Date, endDate: Date): [ApplyClickRank] getScholarJobClicksRanked: [JobClickRank] + getScholarJobClicksRankedWithDateRange(startDate: Date, endDate: Date): [JobClickRank] getScholarEmployerClicksRanked: [EmployerClickRank] + getScholarEmployerClicksRankedWithDateRange(startDate: Date, endDate: Date): [EmployerClickRank] getJobClicksRankedByApply: [RankedJobClick] getScholarClicksBySchool: [ScholarClicksBySchool] + getScholarClicksBySchoolWithDateRange(startDate: Date, endDate: Date): [ScholarClicksBySchool] getEmployerJobPostingsRanking: [EmployerJobPostingRank] + getEmployerJobPostingsRankingWithDateRange(startDate: Date, endDate: Date): [EmployerJobPostingRank] getNumDaysSinceLastJobPostByEmployer: [EmployerLastJobPost] getMostPopularJobTagsByEmployer: [EmployerJobTagRanking] getJobClicksForScholar(scholarId: Int): [ScholarJobClicks] @@ -38,166 +43,166 @@ export const analyticsTypeDefs = gql` getNumberOfActiveScholars: Int getNumberOfAllowedScholars: Int getClicksCustomAnalytics(startDate: Date, endDate: Date, interval: String, clickType: String): [CustomAnalytics] - } + } - type Mutation { + type Mutation { logJobClick(scholarId: Int!, jobId: Int!): JobClick logEmployerClick(scholarId: Int!, employerId: Int!): EmployerClick logApplyClick(scholarId: Int!, jobId: Int!): ApplyClick - } + } - type CustomAnalytics { - date: Date - count: Int - } + type CustomAnalytics { + date: Date + count: Int + } - type ScholarJobClicks { + type ScholarJobClicks { jobId: Int clickTime: String scholarEmail: String scholarName: String jobTitle: String employerName: String - } + } - type ScholarApplyClicks { + type ScholarApplyClicks { jobId: Int clickTime: String scholarEmail: String scholarName: String jobTitle: String employerName: String - } + } - type ScholarEmployerClicks { + type ScholarEmployerClicks { employerId: Int clickTime: String scholarEmail: String scholarName: String employerName: String - } - - type ClickCount { - count: Int - } - - type JobClicksLastWeek { - scholarId: Int - jobId: Int - clickTime: String - scholarEmail: String - scholarName: String - jobTitle: String - employerName: String - } - - type ScholarJobClicks { - scholarId: Int - jobId: Int - clickTime: String - scholarEmail: String - scholarName: String - jobTitle: String - employerName: String - } - - type ScholarApplyClicks { - scholarId: Int - jobId: Int - clickTime: String - scholarEmail: String - scholarName: String - jobTitle: String - employerName: String - } - - type ScholarEmployerClicks { - scholarId: Int - employerId: Int - clickTime: String - scholarEmail: String - scholarName: String - employerName: String - } - - type ApplyClicks { - scholarId: Int - jobId: Int - clickTime: String - scholarEmail: String - scholarName: String - jobTitle: String - employerName: String - } - - type ApplyClick { + } + + type ClickCount { + count: Int + } + + type JobClicksLastWeek { + scholarId: Int + jobId: Int + clickTime: String + scholarEmail: String + scholarName: String + jobTitle: String + employerName: String + } + + type ScholarJobClicks { scholarId: Int jobId: Int clickTime: String - } + scholarEmail: String + scholarName: String + jobTitle: String + employerName: String + } + + type ScholarApplyClicks { + scholarId: Int + jobId: Int + clickTime: String + scholarEmail: String + scholarName: String + jobTitle: String + employerName: String + } - type EmployerJobTagRanking { + type ScholarEmployerClicks { + scholarId: Int + employerId: Int + clickTime: String + scholarEmail: String + scholarName: String + employerName: String + } + + type ApplyClicks { + scholarId: Int + jobId: Int + clickTime: String + scholarEmail: String + scholarName: String + jobTitle: String + employerName: String + } + + type ApplyClick { + scholarId: Int + jobId: Int + clickTime: String + } + + type EmployerJobTagRanking { employer: String tag: String job_count: Int - } + } - type EmployerJobPostingRank { + type EmployerJobPostingRank { employerName: String employerId: String job_posting_click_count: Int - } + } - type EmployerLastJobPost { + type EmployerLastJobPost { employerName: String employerId: String days_since_last_post: Int - } + } + + scalar Date - scalar Date - - type MajorRanking { + type MajorRanking { major: String scholar_count: Int - } + } - type ScholarClicksBySchool { + type ScholarClicksBySchool { school: String scholar_click_count: Int - } + } - type YearRanking { + type YearRanking { year: Int scholar_count: Int - } + } - type JobApplyClickRank { + type JobApplyClickRank { jobId: Int apply_count: Int - } + } - type ApplyClickRank { + type ApplyClickRank { scholarId: Int apply_count: Int scholarName: String scholarEmail: String - } + } - type JobClickRank { + type JobClickRank { scholarId: Int job_count: Int scholarName: String scholarEmail: String - } + } - type EmployerClickRank { - scholarId: Int + type EmployerClickRank { + scholarId: Int employer_count: Int scholarName: String scholarEmail: String - } + } - type JobClick { + type JobClick { employerId: Int jobId: Int jobTitle: String @@ -205,54 +210,54 @@ export const analyticsTypeDefs = gql` clickTime: String scholarName: String scholarEmail: String - } + } - type RankedJobClick { + type RankedJobClick { employerId: Int jobId: Int jobTitle: String employerName: String click_count: Int - } + } - type RankedEmployerClick { + type RankedEmployerClick { employerId: Int employerName: String click_count: Int - } + } - type ApplyClick { + type ApplyClick { scholarId: Int jobId: Int clickTime: String - } + } - type JobTagRanking { + type JobTagRanking { tag: String job_count: Int - } + } - type JobTagRankingByClick { + type JobTagRankingByClick { tag: String click_count: Int - } + } - type JobLocationRanking { + type JobLocationRanking { location: String job_count: Int - } + } - type JobDeadlineRanking { + type JobDeadlineRanking { month: String job_count: Int - } - - type EmployerClick { + } + + type EmployerClick { scholarId: Int employerId: Int employerName: String clickTime: String scholarName: String scholarEmail: String - } -` \ No newline at end of file + } +` diff --git a/frontend/graphql/queries/analyticsQueries.ts b/frontend/graphql/queries/analyticsQueries.ts index c891cb0..a96c796 100644 --- a/frontend/graphql/queries/analyticsQueries.ts +++ b/frontend/graphql/queries/analyticsQueries.ts @@ -1,207 +1,207 @@ -import { gql } from '@apollo/client'; +import { gql } from '@apollo/client' // Query for getting job clicks export const GET_JOB_CLICKS = gql` - query GetJobClicks { - getJobClicks { - jobTitle - employerId - employerName - jobId - clickTime - scholarName - scholarEmail - } - } -`; + query GetJobClicks { + getJobClicks { + jobTitle + employerId + employerName + jobId + clickTime + scholarName + scholarEmail + } + } +` // Query for getting apply clicks export const GET_APPLY_CLICKS = gql` - query GetApplyClicks { - getApplyClicks { - scholarId - jobId - clickTime - scholarName - scholarEmail - jobTitle - employerName - } - } -`; + query GetApplyClicks { + getApplyClicks { + scholarId + jobId + clickTime + scholarName + scholarEmail + jobTitle + employerName + } + } +` // Query for getting employer clicks export const GET_EMPLOYER_CLICKS = gql` - query GetEmployerClicks { - getEmployerClicks { - scholarId - employerId - employerName - clickTime - scholarName - scholarEmail - } - } -`; + query GetEmployerClicks { + getEmployerClicks { + scholarId + employerId + employerName + clickTime + scholarName + scholarEmail + } + } +` // Query for getting ranked job clicks export const GET_JOB_CLICKS_RANKED = gql` - query GetJobClicksRanked { - getJobClicksRanked { - employerId - jobId - jobTitle - employerName - click_count - } - } -`; + query GetJobClicksRanked { + getJobClicksRanked { + employerId + jobId + jobTitle + employerName + click_count + } + } +` // Query for getting job tag rankings export const GET_JOB_TAG_RANKING = gql` - query GetJobTagRanking { - getJobTagRanking { - tag - job_count - } + query GetJobTagRanking { + getJobTagRanking { + tag + job_count } -`; + } +` // Query for getting job tag ranking by clicks export const GET_JOB_TAG_RANKING_BY_CLICKS = gql` - query GetJobTagRankingByClicks { - getJobTagRankingByClicks { - tag - click_count - } + query GetJobTagRankingByClicks { + getJobTagRankingByClicks { + tag + click_count } -`; + } +` // Query for getting job location ranking export const GET_JOB_LOCATION_RANKING = gql` - query GetJobLocationRanking { - getJobLocationRanking { - location - job_count - } + query GetJobLocationRanking { + getJobLocationRanking { + location + job_count } -`; + } +` // Query for getting job deadline ranking by month export const GET_JOB_DEADLINE_RANKING_BY_MONTH = gql` - query GetJobDeadlineRankingByMonth { - getJobDeadlineRankingByMonth { - month - job_count - } + query GetJobDeadlineRankingByMonth { + getJobDeadlineRankingByMonth { + month + job_count } -`; + } +` // Query for getting scholars ranked by major export const GET_SCHOLARS_RANKED_BY_MAJOR = gql` - query GetScholarsRankedByMajor { - getScholarsRankedByMajor { - major - scholar_count - } + query GetScholarsRankedByMajor { + getScholarsRankedByMajor { + major + scholar_count } -`; + } +` // Query for getting scholars ranked by year export const GET_SCHOLARS_RANKED_BY_YEAR = gql` - query GetScholarsRankedByYear { - getScholarsRankedByYear { - year - scholar_count - } + query GetScholarsRankedByYear { + getScholarsRankedByYear { + year + scholar_count } -`; + } +` // Query for getting the percentage of scholars with allowed notifications export const GET_PERCENTAGE_OF_SCHOLARS_WITH_ALLOWED_NOTIFICATIONS = gql` - query GetPercentageOfScholarsWithAllowedNotifications { - getPercentageOfScholarsWithAllowedNotifications - } -`; + query GetPercentageOfScholarsWithAllowedNotifications { + getPercentageOfScholarsWithAllowedNotifications + } +` // Query for getting scholar apply clicks ranked export const GET_SCHOLAR_APPLY_CLICKS_RANKED = gql` - query GetScholarApplyClicksRanked { - getScholarApplyClicksRanked { - scholarId - apply_count - scholarName - scholarEmail - } - } -`; + query GetScholarApplyClicksRanked { + getScholarApplyClicksRanked { + scholarId + apply_count + scholarName + scholarEmail + } + } +` // Query for getting scholar job clicks ranked export const GET_SCHOLAR_JOB_CLICKS_RANKED = gql` - query GetScholarJobClicksRanked { - getScholarJobClicksRanked { - scholarId - job_count - scholarName - scholarEmail - } - } -`; + query GetScholarJobClicksRanked { + getScholarJobClicksRanked { + scholarId + job_count + scholarName + scholarEmail + } + } +` // Query for getting scholar employer clicks ranked export const GET_SCHOLAR_EMPLOYER_CLICKS_RANKED = gql` - query GetScholarEmployerClicksRanked { - getScholarEmployerClicksRanked { - scholarId - employer_count - scholarName - scholarEmail - } - } -`; + query GetScholarEmployerClicksRanked { + getScholarEmployerClicksRanked { + scholarId + employer_count + scholarName + scholarEmail + } + } +` // Query for getting employer job postings ranking export const GET_EMPLOYER_JOB_POSTINGS_RANKING = gql` - query GetEmployerJobPostingsRanking { - getEmployerJobPostingsRanking { - employerName - employerId - job_posting_click_count - } - } -`; + query GetEmployerJobPostingsRanking { + getEmployerJobPostingsRanking { + employerName + employerId + job_posting_click_count + } + } +` // Query for getting the number of days since last job post by employer export const GET_NUM_DAYS_SINCE_LAST_JOB_POST_BY_EMPLOYER = gql` - query GetNumDaysSinceLastJobPostByEmployer { - getNumDaysSinceLastJobPostByEmployer { - employerName - employerId - days_since_last_post - } - } -`; + query GetNumDaysSinceLastJobPostByEmployer { + getNumDaysSinceLastJobPostByEmployer { + employerName + employerId + days_since_last_post + } + } +` // Query for getting the most popular job tags by employer export const GET_MOST_POPULAR_JOB_TAGS_BY_EMPLOYER = gql` - query GetMostPopularJobTagsByEmployer { - getMostPopularJobTagsByEmployer { - employer - tag - job_count - } - } -`; + query GetMostPopularJobTagsByEmployer { + getMostPopularJobTagsByEmployer { + employer + tag + job_count + } + } +` // Query for getting scholar clicks by school export const GET_SCHOLAR_CLICKS_BY_SCHOOL = gql` - query GetScholarClicksBySchool { - getScholarClicksBySchool { - school - scholar_click_count - } + query GetScholarClicksBySchool { + getScholarClicksBySchool { + school + scholar_click_count } -`; + } +` export const GET_ALL_CLICK_COUNTS = gql` query GetAllClickCounts { @@ -233,7 +233,7 @@ export const GET_ALL_CLICK_COUNTS = gql` count } } -`; +` export const GET_JOB_CLICKS_FOR_SCHOLAR = gql` query GetJobClicksForScholar($scholarId: Int!) { @@ -246,8 +246,7 @@ export const GET_JOB_CLICKS_FOR_SCHOLAR = gql` employerName } } -`; - +` export const GET_APPLY_CLICKS_FOR_SCHOLAR = gql` query GetApplyClicksForScholar($scholarId: Int!) { @@ -260,7 +259,7 @@ export const GET_APPLY_CLICKS_FOR_SCHOLAR = gql` employerName } } -`; +` export const GET_EMPLOYER_CLICKS_FOR_SCHOLAR = gql` query GetEmployerClicksForScholar($scholarId: Int!) { @@ -272,25 +271,186 @@ export const GET_EMPLOYER_CLICKS_FOR_SCHOLAR = gql` employerName } } -`; +` export const GET_NUMBER_OF_ACTIVE_SCHOLARS = gql` query GetNumberOfActiveScholars { getNumberOfActiveScholars } -`; +` export const GET_NUMBER_OF_ALLOWED_SCHOLARS = gql` query GetNumberOfAllowedScholars { getNumberOfAllowedScholars } -`; +` export const GET_CLICKS_CUSTOM_ANALYTICS = gql` - query GetClicksCustomAnalytics($startDate: Date!, $endDate: Date!, $interval: String!, $clickType: String!) { - getClicksCustomAnalytics(startDate: $startDate, endDate: $endDate, interval: $interval, clickType: $clickType) { - date - count - } + query GetClicksCustomAnalytics($startDate: Date!, $endDate: Date!, $interval: String!, $clickType: String!) { + getClicksCustomAnalytics(startDate: $startDate, endDate: $endDate, interval: $interval, clickType: $clickType) { + date + count + } + } +` + +export const GET_ANALYTICS_DASHBOARD_DATA = gql` + query GetAnalyticsDashboardData($startDate: Date!, $endDate: Date!) { + jobTagsRankedByJobCount: getJobTagRanking { + tag + job_count + } + jobLocationsRankedByJobCount: getJobLocationRanking { + location + job_count + } + jobDeadlinesAsMonthRankedByJobCount: getJobDeadlineRankingByMonth { + month + job_count } - `; + daysSinceLastJobPostByEmployer: getNumDaysSinceLastJobPostByEmployer { + employerName + days_since_last_post + } + scholarsRankedByMajor: getScholarsRankedByMajor { + major + scholar_count + } + scholarsRankedByYear: getScholarsRankedByYear { + year + scholar_count + } + jobTagClicks: getJobTagRankingByClicksWithDateRange(startDate: $startDate, endDate: $endDate) { + tag + click_count + } + employerJobPostingClicks: getEmployerJobPostingsRankingWithDateRange(startDate: $startDate, endDate: $endDate) { + employerName + job_posting_click_count + } + scholarClicksBySchool: getScholarClicksBySchoolWithDateRange(startDate: $startDate, endDate: $endDate) { + school + scholar_click_count + } + scholarJobClicks: getScholarJobClicksRankedWithDateRange(startDate: $startDate, endDate: $endDate) { + scholarName + job_count + } + scholarApplyClicks: getScholarApplyClicksRankedWithDateRange(startDate: $startDate, endDate: $endDate) { + scholarName + apply_count + } + scholarEmployerClicks: getScholarEmployerClicksRankedWithDateRange(startDate: $startDate, endDate: $endDate) { + scholarName + employer_count + } + jobClicksDaily: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "daily" + clickType: "job" + ) { + date + count + } + jobClicksWeekly: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "weekly" + clickType: "job" + ) { + date + count + } + jobClicksMonthly: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "monthly" + clickType: "job" + ) { + date + count + } + jobClicksYearly: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "yearly" + clickType: "job" + ) { + date + count + } + applyClicksDaily: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "daily" + clickType: "apply" + ) { + date + count + } + applyClicksWeekly: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "weekly" + clickType: "apply" + ) { + date + count + } + applyClicksMonthly: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "monthly" + clickType: "apply" + ) { + date + count + } + applyClicksYearly: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "yearly" + clickType: "apply" + ) { + date + count + } + employerClicksDaily: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "daily" + clickType: "employer" + ) { + date + count + } + employerClicksWeekly: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "weekly" + clickType: "employer" + ) { + date + count + } + employerClicksMonthly: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "monthly" + clickType: "employer" + ) { + date + count + } + employerClicksYearly: getClicksCustomAnalytics( + startDate: $startDate + endDate: $endDate + interval: "yearly" + clickType: "employer" + ) { + date + count + } + } +` diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 951fc43..4f97330 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -41,7 +41,7 @@ "@types/file-saver": "^2.0.7", "@vercel/analytics": "^1.0.1", "bcrypt": "^5.1.0", - "chart.js": "^4.3.0", + "chart.js": "^4.4.4", "dayjs": "^1.11.6", "dotenv": "^16.0.3", "embla-carousel-react": "^7.0.5", @@ -59,6 +59,7 @@ "react": "18.2.0", "react-chartjs-2": "^5.2.0", "react-dom": "18.2.0", + "react-google-charts": "^4.0.1", "react-hook-form": "^7.41.3", "react-icons": "^4.7.1", "react-responsive": "^9.0.2", @@ -2276,14 +2277,14 @@ } }, "node_modules/chart.js": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.1.tgz", - "integrity": "sha512-QHuISG3hTJ0ftq0I0f5jqH9mNVO9bqG8P+zvMOVslgKajQVvFEX7QAhYNJ+QEmw+uYTwo8XpTimaB82oeTWjxw==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz", + "integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==", "dependencies": { "@kurkle/color": "^0.3.0" }, "engines": { - "pnpm": ">=7" + "pnpm": ">=8" } }, "node_modules/chownr": { @@ -5951,6 +5952,15 @@ "react": ">= 16.8 || 18.0.0" } }, + "node_modules/react-google-charts": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-google-charts/-/react-google-charts-4.0.1.tgz", + "integrity": "sha512-V/hcMcNuBgD5w49BYTUDye+bUKaPmsU5vy/9W/Nj2xEeGn+6/AuH9IvBkbDcNBsY00cV9OeexdmgfI5RFHgsXQ==", + "peerDependencies": { + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + } + }, "node_modules/react-hook-form": { "version": "7.45.2", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.45.2.tgz", @@ -8962,9 +8972,9 @@ } }, "chart.js": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.1.tgz", - "integrity": "sha512-QHuISG3hTJ0ftq0I0f5jqH9mNVO9bqG8P+zvMOVslgKajQVvFEX7QAhYNJ+QEmw+uYTwo8XpTimaB82oeTWjxw==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz", + "integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==", "requires": { "@kurkle/color": "^0.3.0" } @@ -11655,6 +11665,12 @@ "prop-types": "^15.8.1" } }, + "react-google-charts": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-google-charts/-/react-google-charts-4.0.1.tgz", + "integrity": "sha512-V/hcMcNuBgD5w49BYTUDye+bUKaPmsU5vy/9W/Nj2xEeGn+6/AuH9IvBkbDcNBsY00cV9OeexdmgfI5RFHgsXQ==", + "requires": {} + }, "react-hook-form": { "version": "7.45.2", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.45.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 48b9f22..99e0e22 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -42,7 +42,7 @@ "@types/file-saver": "^2.0.7", "@vercel/analytics": "^1.0.1", "bcrypt": "^5.1.0", - "chart.js": "^4.3.0", + "chart.js": "^4.4.4", "dayjs": "^1.11.6", "dotenv": "^16.0.3", "embla-carousel-react": "^7.0.5", @@ -60,6 +60,7 @@ "react": "18.2.0", "react-chartjs-2": "^5.2.0", "react-dom": "18.2.0", + "react-google-charts": "^4.0.1", "react-hook-form": "^7.41.3", "react-icons": "^4.7.1", "react-responsive": "^9.0.2", diff --git a/frontend/pages/Admin.tsx b/frontend/pages/Admin.tsx index babb739..826b049 100644 --- a/frontend/pages/Admin.tsx +++ b/frontend/pages/Admin.tsx @@ -14,6 +14,7 @@ import EditJobButton from "../src/components/admin/EditJobButton"; import { Analytics } from "@vercel/analytics/react"; import AnalyticsPage from "./Analytics"; import AnalyticsButton from "../src/components/admin/AnalyticsButton"; +import DashboardButton from "../src/components/admin/DashboardButton"; // To ensure unauthenticated people don't access @@ -75,7 +76,8 @@ export default function Admin() {
{firstHeading} | +{secondHeading} | +
---|---|
{row[0]} | +{row[1]} | +
No Data | ++ |