From e48c6b51448358a5b26627edc2242dba068b370a Mon Sep 17 00:00:00 2001 From: alyzaraviandi Date: Sat, 18 May 2024 06:47:57 +0700 Subject: [PATCH] add table --- code_context.txt | 1704 ------------------- get_code_context.sh | 75 - index.html | 26 +- script/filter.js | 36 +- script/leadingStore.js | 63 + script/priceInfluencePurchasingBehaviour.js | 13 +- script/productVarietyRevenue.js | 12 +- script/productVarietyTransaction.js | 12 +- script/totalRevenue.js | 12 +- script/totalTransaction.js | 14 +- script/trendDay.js | 14 +- styles.css | 20 +- tempCodeRunnerFile.js | 14 - 13 files changed, 132 insertions(+), 1883 deletions(-) delete mode 100644 code_context.txt delete mode 100644 get_code_context.sh create mode 100644 script/leadingStore.js delete mode 100644 tempCodeRunnerFile.js diff --git a/code_context.txt b/code_context.txt deleted file mode 100644 index e1efc38..0000000 --- a/code_context.txt +++ /dev/null @@ -1,1704 +0,0 @@ -// File: data_schema/new_transaction_schema.json -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "transaction_id": { - "type": "string" - }, - "transaction_date": { - "type": "string" - }, - "transaction_time": { - "type": "string" - }, - "transaction_qty": { - "type": "string" - }, - "store_id": { - "type": "string" - }, - "store_location": { - "type": "string" - }, - "product_id": { - "type": "string" - }, - "unit_price": { - "type": "string" - }, - "product_category": { - "type": "string" - }, - "product_type": { - "type": "string" - }, - "product_detail": { - "type": "string" - }, - "outliers": { - "type": "string" - }, - "day_of_week": { - "type": "string" - }, - "month_of_year": { - "type": "string" - } - }, - "required": [ - "transaction_id", - "transaction_date", - "transaction_time", - "transaction_qty", - "store_id", - "store_location", - "product_id", - "unit_price", - "product_category", - "product_type", - "product_detail", - "outliers", - "day_of_week", - "month_of_year" - ] - } -// File: data_schema/trend_data_schema.json -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "dayOfWeek": { - "type": "string" - }, - "monthOfYear": { - "type": "string" - }, - "productCategory": { - "type": "string" - }, - "productType": { - "type": "string" - }, - "storeLocation": { - "type": "string" - }, - "totalQty": { - "type": "integer" - }, - "totalRevenue": { - "type": "integer" - } - }, - "required": [ - "dayOfWeek", - "monthOfYear", - "productCategory", - "productType", - "storeLocation", - "totalQty", - "totalRevenue" - ] - } -// File: script/filter.js -// filter.js -export function filterData(originalData) { - return originalData.filter((item) => { - if (!document.querySelector(`input[value="${item.dayOfWeek}"]`).checked) { - return false; - } - if (!document.querySelector(`input[value="${item.monthOfYear}"]`).checked) { - return false; - } - if (!document.querySelector(`input[value="${item.productCategory}"]`).checked) { - return false; - } - if (!document.querySelector(`input[value="${item.storeLocation}"]`).checked) { - return false; - } - return true; - }); - } - -// File: script/totalRevenue.js -import { filterData } from './filter.js'; -import productColors from "../colors.js"; - -window.addEventListener('load', function() { - let revenueChart; // Declare revenueChart variable outside the updateChart function - - // Fetch the data from the JSON file - fetch("data/trend_data.json") - .then((response) => response.json()) - .then((originalData) => { - // Function to update the chart - function updateChart(data) { - // Create an object to store the total revenues for each product type - let totalRevenues = {}; - - // Iterate over the data - for (let item of data) { - // If the product type is not in the totalRevenues object, add it - if (!(item.productType in totalRevenues)) { - totalRevenues[item.productType] = 0; - } - - // Add the total revenue for this item to the total for its product type - totalRevenues[item.productType] += item.totalRevenue; - } - - // Extract product types and total revenues from the totalRevenues object - let productTypes = Object.keys(totalRevenues); - let revenues = Object.values(totalRevenues); - - // Create an array of colors based on the product types - let backgroundColors = productTypes.map( - (type) => productColors[type] - ); - - // Destroy the old chart if it exists - if (revenueChart) { - revenueChart.destroy(); - } - - // Create the chart - const ctx = document - .getElementById("totalRevenue") // Make sure to change the canvas id to the id of the canvas for the revenue chart - .getContext("2d"); - const chartData = { - labels: productTypes, - datasets: [ - { - label: "Total Revenues", - data: revenues, - backgroundColor: backgroundColors, - hoverOffset: 4, - }, - ], - }; - revenueChart = new Chart(ctx, { - type: "pie", - data: chartData, - options: { - plugins: { - datalabels: { - formatter: (value, ctx) => { - const label = ctx.chart.data.labels[ctx.dataIndex]; - return `${label}: ${value}`; - }, - color: "#fff", - font: { - weight: "bold", - }, - anchor: 'end', - align: 'end', - }, - legend: { - display: true, - position: 'right', - labels: { - usePointStyle: true, - pointStyle: 'circle', - }, - } - } - }, - }); - } - - // Initial chart creation - updateChart(originalData); - - // Add event listener to each checkbox - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - // Filter out the data that matches the unchecked property - const filteredData = filterData(originalData); - - // Update the chart - updateChart(filteredData); - }); - }); - }) - .catch((error) => console.error("Error:", error)); -}); - -// File: script/totalTransaction.js -import { filterData } from './filter.js'; -import productColors from "../colors.js"; - -window.addEventListener('load', function() { - let pieChart; - - fetch("data/trend_data.json") - .then((response) => response.json()) - .then((originalData) => { - function updateChart(data) { - let totalQuantities = {}; - for (let item of data) { - // If the product type is not in the totalQuantities object, add it - if (!(item.productType in totalQuantities)) { - totalQuantities[item.productType] = 0; - } - - // Add the total quantity for this item to the total for its product type - totalQuantities[item.productType] += item.totalQty; - } - - // Extract product types and total quantities from the totalQuantities object - let productTypes = Object.keys(totalQuantities); - let quantities = Object.values(totalQuantities); - - // Create an array of colors based on the product types - let backgroundColors = productTypes.map( - (type) => productColors[type] - ); - - // Destroy the old chart if it exists - if (pieChart) { - pieChart.destroy(); - } - - // Create the chart - const ctx = document - .getElementById("totalTransaction") - .getContext("2d"); - const chartData = { - labels: productTypes, - datasets: [ - { - label: "Total Transactions", - data: quantities, - backgroundColor: backgroundColors, - hoverOffset: 4, - }, - ], - }; - pieChart = new Chart(ctx, { - type: "pie", - data: chartData, - options: { - plugins: { - datalabels: { - formatter: (value, ctx) => { - let sum = ctx.chart.data.datasets[0].data.reduce((a, b) => a + b, 0); - let percentage = (value * 100 / sum).toFixed(2) + "%"; - return percentage; - }, - color: "#fff", - font: { - weight: "bold", - }, - filter: (value, ctx) => { - let sum = ctx.chart.data.datasets[0].data.reduce((a, b) => a + b, 0); - let percentage = (value * 100 / sum); - return percentage > 10; // Only display labels for slices > 5% - }, - }, - legend: { - display: true, - position: 'right', - labels: { - usePointStyle: true, - pointStyle: 'circle', - }, - } - } - }, - plugins: [ChartDataLabels], - }); - } - - // Initial chart creation - updateChart(originalData); - - // Add event listener to each checkbox - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - // Filter out the data that matches the unchecked property - const filteredData = filterData(originalData); - - updateChart(filteredData); - }); - }); - }) - .catch((error) => console.error("Error:", error)); -}); - -// File: script/trendDay.js -import productColors from "../colors.js"; -import { filterData } from './filter.js'; - -window.addEventListener('load', function() { - const ctx = document.getElementById("trendDay").getContext("2d"); - - fetch("data/trend_data.json") - .then((response) => response.json()) - .then((originalData) => { - // Function to update the chart - function updateChart(data) { - // Get unique product types - const productTypes = [ - ...new Set(data.map((item) => item.productType)), - ]; - - // Prepare the datasets - const datasets = productTypes.map((productType) => { - // Filter data for this product type - const productData = data.filter( - (item) => item.productType === productType - ); - - // Sum up totalQty for each dayOfWeek - const totalQtyByDay = [ - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - "Sunday", - ].map((dayOfWeek) => { - const dayData = productData.filter( - (item) => item.dayOfWeek === dayOfWeek - ); - const totalQty = dayData.reduce( - (sum, item) => sum + item.totalQty, - 0 - ); - return totalQty; - }); - - return { - label: productType, - data: totalQtyByDay, - backgroundColor: productColors[productType], - borderColor: productColors[productType], - borderWidth: 1, - }; - }); - - // Create or update the chart - if (window.chart) { - window.chart.data.datasets = datasets; - window.chart.update(); - } else { - window.chart = new Chart(ctx, { - type: "bar", - data: { - labels: [ - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - "Sunday", - ], - datasets: datasets, - }, - options: { - indexAxis: "y", - scales: { - x: { - stacked: true, - }, - y: { - stacked: true, - }, - }, - responsive: true, - maintainAspectRatio: false, - }, - }); - } - } - - // Initial chart creation - updateChart(originalData); - - // Add event listener to each checkbox - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - // Filter out the data that matches the unchecked property - const filteredData = filterData(originalData); - - // Update the chart - updateChart(filteredData); - }); - }); - }) - .catch((error) => console.error("Error:", error)); - - // Resize function - window.addEventListener('resize', () => { - let chartElement = document.getElementById('trendDay'); - let parent = chartElement.parentElement; - - chartElement.width = parent.offsetWidth; - chartElement.height = parent.offsetHeight; - - window.chart.resize(); - }); -}); - -// File: about.html - - - - - - -
- - - - -
-
Our Members
- -
- -
-
-
- Person 1 -

John Doe
Institution A

-
-
- Person 2 -

Jane Smith
Institution B

-
-
- Person 3 -

Michael Johnson
Institution C

-
-
- Person 1 -

John Doe
Institution A

-
-
- Person 2 -

Jane Smith
Institution B

-
-
- Person 3 -

Michael Johnson
Institution C

-
-
- Person 1 -

John Doe
Institution A

-
-
- Person 2 -

Jane Smith
Institution B

-
-
- Person 3 -

Michael Johnson
Institution C

-
-
- Person 1 -

John Doe
Institution A

-
-
- Person 2 -

Jane Smith
Institution B

-
-
- Person 3 -

Michael Johnson
Institution C

-
-
-
-
-
- - - - - - -// File: code_context.txt - -// File: colors.js -const productColors = { - "Drip coffee": "rgba(101, 67, 33, 1)", - "Organic brewed coffee": "rgba(139, 69, 19, 1)", - "Gourmet brewed coffee": "rgba(160, 82, 45, 1)", - "Premium brewed coffee": "rgba(210, 105, 30, 1)", - "Barista Espresso": "rgba(205, 133, 63, 1)", - "Brewed herbal tea": "rgba(50, 205, 50, 1)", - "Brewed Green tea": "rgba(34, 139, 34, 1)", - "Brewed Black tea": "rgba(152, 251, 152, 1)", - "Brewed Chai tea": "rgba(124, 252, 0, 1)", - "Hot Chocolate": "Black", - "Regular syrup": "rgba(255, 0, 0, 1)", - "Sugar free syrup": "rgba(139, 0, 0, 1)", - "Biscotti": "rgba(255, 165, 0, 1)", - "Scone": "rgba(255, 140, 0, 1)", - "Pastry": "rgba(255, 69, 0, 1)" - }; - - export default productColors; - -// File: contact.html - - - - - - -
- - - -
- - -
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
- Profile Picture -
-

Name

-

Address

- -

Phone Number

-
-
-
-
- - - - - -// File: dataservice.js -const fs = require('fs'); - -// Load the data into memory -let data = JSON.parse(fs.readFileSync('data/new_transaction.json', 'utf8')); - -// The data service -// The data service -let dataService = { - // A function that returns the total transaction quantity and total revenue for each product type on each day of the week - getDailyProductQtyAndRevenue: function() { - let dailyProductQtyAndRevenue = {}; - data.forEach(function(transaction) { - let dayOfWeek = new Date(transaction.transaction_date).toLocaleString('en-US', { weekday: 'long' }); - let monthOfYear = new Date(transaction.transaction_date).toLocaleString('en-US', { month: 'long' }); - let key = dayOfWeek + '|' + monthOfYear + '|' + transaction.product_category + '|' + transaction.product_type + '|' + transaction.store_location; - if (dailyProductQtyAndRevenue[key]) { - dailyProductQtyAndRevenue[key].totalQty += parseInt(transaction.transaction_qty); - dailyProductQtyAndRevenue[key].totalRevenue += parseInt(transaction.transaction_qty) * parseFloat(transaction.unit_price); - } else { - dailyProductQtyAndRevenue[key] = { - totalQty: parseInt(transaction.transaction_qty), - totalRevenue: parseInt(transaction.transaction_qty) * parseFloat(transaction.unit_price) - }; - } - }); - - // Convert the results to an array and sort by day of the week - let dailyProductQtyAndRevenueArray = Object.entries(dailyProductQtyAndRevenue).map(function(entry) { - let [dayOfWeek, monthOfYear, productCategory, productType, storeLocation] = entry[0].split('|'); - return { - dayOfWeek: dayOfWeek, - monthOfYear: monthOfYear, - productCategory: productCategory, - productType: productType, - storeLocation: storeLocation, - totalQty: entry[1].totalQty, - totalRevenue: Math.round(entry[1].totalRevenue) // round to nearest integer - }; - }); - dailyProductQtyAndRevenueArray.sort(function(a, b) { - let daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; - return daysOfWeek.indexOf(a.dayOfWeek) - daysOfWeek.indexOf(b.dayOfWeek); - }); - - return dailyProductQtyAndRevenueArray; - }, - - // Other functions for other queries... -}; - -// Get the processed data -const trendData = dataService.getDailyProductQtyAndRevenue(); - -// Convert data to JSON string -const trendDataJSON = JSON.stringify(trendData, null, 2); - -// Write JSON string to a new file named trend_data.json in the data directory -fs.writeFile('data/trend_data.json', trendDataJSON, 'utf8', (err) => { - if (err) { - console.error('Error writing file:', err); - return; - } - console.log('Trend data has been written to data/trend_data.json'); -}); - -module.exports = dataService; - -// File: footer.html -
- - -
-// File: footer.js -document.addEventListener("DOMContentLoaded", function() { - // Create a new footer element - var footer = document.createElement("footer"); - - // Create a div element for the copyright text - var copyrightDiv = document.createElement("div"); - copyrightDiv.classList.add("copyright"); - - // Create a paragraph element for the copyright text - var copyrightText = document.createElement("p"); - copyrightText.innerHTML = "© 2024 Team 20 Semarang. All rights reserved."; - - // Append the paragraph element to the div - copyrightDiv.appendChild(copyrightText); - - // Append the div to the footer element - footer.appendChild(copyrightDiv); - - // Get the footer element by its id - var footerContainer = document.getElementById("footer"); - - // Append the footer element to the footer container - footerContainer.appendChild(footer); -}); - -// File: index.html - - - - - - -
- - - - -
-
-

Dashboard Coffee Shop

- -
-
-

Total Transaction

-

214,065

-
-
-

Total Revenue

-

697,374

-
-
-
- -
-
- Filter -
-
-
-
-
-

Product Category

- - - - - -
-
-
-

Store

- - - -
-
-
-

Month

- - - - - - -
-
-
-

Day

- - - - - - - -
-
-
-

Daily Transaction per Product Type

-
- -
-
-
-

Total Transactions per Product Type

- -
-
-

Total Revenue per Product Type

- -
-
-
-
-
-
-
- - - - - - - - - - - - - - -// File: navigation.html -
- -
- -// File: navigation.js -document.addEventListener("DOMContentLoaded", function () { - var xhr = new XMLHttpRequest(); - xhr.open('GET', 'navigation.html', true); - xhr.onreadystatechange = function () { - if (xhr.readyState == 4 && xhr.status == 200) { - document.getElementById('navigation').innerHTML = xhr.responseText; - highlightActiveLink(); - } - }; - xhr.send(); -}); - -function highlightActiveLink() { - var navLinks = document.querySelectorAll('.menu a'); - var currentPage = window.location.href.split('/').pop(); - - navLinks.forEach(function(link) { - var linkPage = link.getAttribute('href'); - - if (linkPage === currentPage) { - link.style.textDecoration = 'underline'; - } - }); -} - -// File: package-lock.json -{ - "name": "Copy", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "chart.js-plugin-labels-dv": "^5.0.1-beta", - "papaparse": "^5.4.1" - } - }, - "node_modules/@kurkle/color": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", - "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", - "peer": true - }, - "node_modules/chart.js": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.2.tgz", - "integrity": "sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==", - "peer": true, - "dependencies": { - "@kurkle/color": "^0.3.0" - }, - "engines": { - "pnpm": ">=8" - } - }, - "node_modules/chart.js-plugin-labels-dv": { - "version": "5.0.1-beta", - "resolved": "https://registry.npmjs.org/chart.js-plugin-labels-dv/-/chart.js-plugin-labels-dv-5.0.1-beta.tgz", - "integrity": "sha512-Z0IZ5MpysGIpQ0awiPqxxvont0oQJcZZJnn23ujpbtsETQ0EmQGOGOU7zzTPuuLBLhVPYP+o8OLV+BKmjcoDJA==", - "peerDependencies": { - "chart.js": "^4.3.3" - } - }, - "node_modules/papaparse": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", - "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==" - } - } -} - -// File: package.json -{ - "dependencies": { - "chart.js-plugin-labels-dv": "^5.0.1-beta", - "papaparse": "^5.4.1" - } -} - -// File: script/filter.js -// filter.js -export function filterData(originalData) { - return originalData.filter((item) => { - if (!document.querySelector(`input[value="${item.dayOfWeek}"]`).checked) { - return false; - } - if (!document.querySelector(`input[value="${item.monthOfYear}"]`).checked) { - return false; - } - if (!document.querySelector(`input[value="${item.productCategory}"]`).checked) { - return false; - } - if (!document.querySelector(`input[value="${item.storeLocation}"]`).checked) { - return false; - } - return true; - }); - } - -// File: script/totalRevenue.js -import { filterData } from './filter.js'; -import productColors from "../colors.js"; - -window.addEventListener('load', function() { - let revenueChart; // Declare revenueChart variable outside the updateChart function - - // Fetch the data from the JSON file - fetch("data/trend_data.json") - .then((response) => response.json()) - .then((originalData) => { - // Function to update the chart - function updateChart(data) { - // Create an object to store the total revenues for each product type - let totalRevenues = {}; - - // Iterate over the data - for (let item of data) { - // If the product type is not in the totalRevenues object, add it - if (!(item.productType in totalRevenues)) { - totalRevenues[item.productType] = 0; - } - - // Add the total revenue for this item to the total for its product type - totalRevenues[item.productType] += item.totalRevenue; - } - - // Extract product types and total revenues from the totalRevenues object - let productTypes = Object.keys(totalRevenues); - let revenues = Object.values(totalRevenues); - - // Create an array of colors based on the product types - let backgroundColors = productTypes.map( - (type) => productColors[type] - ); - - // Destroy the old chart if it exists - if (revenueChart) { - revenueChart.destroy(); - } - - // Create the chart - const ctx = document - .getElementById("totalRevenue") // Make sure to change the canvas id to the id of the canvas for the revenue chart - .getContext("2d"); - const chartData = { - labels: productTypes, - datasets: [ - { - label: "Total Revenues", - data: revenues, - backgroundColor: backgroundColors, - hoverOffset: 4, - }, - ], - }; - revenueChart = new Chart(ctx, { - type: "pie", - data: chartData, - options: { - plugins: { - datalabels: { - formatter: (value, ctx) => { - const label = ctx.chart.data.labels[ctx.dataIndex]; - return `${label}: ${value}`; - }, - color: "#fff", - font: { - weight: "bold", - }, - anchor: 'end', - align: 'end', - }, - legend: { - display: true, - position: 'right', - labels: { - usePointStyle: true, - pointStyle: 'circle', - }, - } - } - }, - }); - } - - // Initial chart creation - updateChart(originalData); - - // Add event listener to each checkbox - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - // Filter out the data that matches the unchecked property - const filteredData = filterData(originalData); - - // Update the chart - updateChart(filteredData); - }); - }); - }) - .catch((error) => console.error("Error:", error)); -}); - -// File: script/totalTransaction.js -import { filterData } from './filter.js'; -import productColors from "../colors.js"; - -window.addEventListener('load', function() { - let pieChart; - - fetch("data/trend_data.json") - .then((response) => response.json()) - .then((originalData) => { - function updateChart(data) { - let totalQuantities = {}; - for (let item of data) { - // If the product type is not in the totalQuantities object, add it - if (!(item.productType in totalQuantities)) { - totalQuantities[item.productType] = 0; - } - - // Add the total quantity for this item to the total for its product type - totalQuantities[item.productType] += item.totalQty; - } - - // Extract product types and total quantities from the totalQuantities object - let productTypes = Object.keys(totalQuantities); - let quantities = Object.values(totalQuantities); - - // Create an array of colors based on the product types - let backgroundColors = productTypes.map( - (type) => productColors[type] - ); - - // Destroy the old chart if it exists - if (pieChart) { - pieChart.destroy(); - } - - // Create the chart - const ctx = document - .getElementById("totalTransaction") - .getContext("2d"); - const chartData = { - labels: productTypes, - datasets: [ - { - label: "Total Transactions", - data: quantities, - backgroundColor: backgroundColors, - hoverOffset: 4, - }, - ], - }; - pieChart = new Chart(ctx, { - type: "pie", - data: chartData, - options: { - plugins: { - datalabels: { - formatter: (value, ctx) => { - let sum = ctx.chart.data.datasets[0].data.reduce((a, b) => a + b, 0); - let percentage = (value * 100 / sum).toFixed(2) + "%"; - return percentage; - }, - color: "#fff", - font: { - weight: "bold", - }, - filter: (value, ctx) => { - let sum = ctx.chart.data.datasets[0].data.reduce((a, b) => a + b, 0); - let percentage = (value * 100 / sum); - return percentage > 10; // Only display labels for slices > 5% - }, - }, - legend: { - display: true, - position: 'right', - labels: { - usePointStyle: true, - pointStyle: 'circle', - }, - } - } - }, - plugins: [ChartDataLabels], - }); - } - - // Initial chart creation - updateChart(originalData); - - // Add event listener to each checkbox - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - // Filter out the data that matches the unchecked property - const filteredData = filterData(originalData); - - updateChart(filteredData); - }); - }); - }) - .catch((error) => console.error("Error:", error)); -}); - -// File: script/trendDay.js -import productColors from "../colors.js"; -import { filterData } from './filter.js'; - -window.addEventListener('load', function() { - const ctx = document.getElementById("trendDay").getContext("2d"); - - fetch("data/trend_data.json") - .then((response) => response.json()) - .then((originalData) => { - // Function to update the chart - function updateChart(data) { - // Get unique product types - const productTypes = [ - ...new Set(data.map((item) => item.productType)), - ]; - - // Prepare the datasets - const datasets = productTypes.map((productType) => { - // Filter data for this product type - const productData = data.filter( - (item) => item.productType === productType - ); - - // Sum up totalQty for each dayOfWeek - const totalQtyByDay = [ - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - "Sunday", - ].map((dayOfWeek) => { - const dayData = productData.filter( - (item) => item.dayOfWeek === dayOfWeek - ); - const totalQty = dayData.reduce( - (sum, item) => sum + item.totalQty, - 0 - ); - return totalQty; - }); - - return { - label: productType, - data: totalQtyByDay, - backgroundColor: productColors[productType], - borderColor: productColors[productType], - borderWidth: 1, - }; - }); - - // Create or update the chart - if (window.chart) { - window.chart.data.datasets = datasets; - window.chart.update(); - } else { - window.chart = new Chart(ctx, { - type: "bar", - data: { - labels: [ - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - "Sunday", - ], - datasets: datasets, - }, - options: { - indexAxis: "y", - scales: { - x: { - stacked: true, - }, - y: { - stacked: true, - }, - }, - responsive: true, - maintainAspectRatio: false, - }, - }); - } - } - - // Initial chart creation - updateChart(originalData); - - // Add event listener to each checkbox - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - // Filter out the data that matches the unchecked property - const filteredData = filterData(originalData); - - // Update the chart - updateChart(filteredData); - }); - }); - }) - .catch((error) => console.error("Error:", error)); - - // Resize function - window.addEventListener('resize', () => { - let chartElement = document.getElementById('trendDay'); - let parent = chartElement.parentElement; - - chartElement.width = parent.offsetWidth; - chartElement.height = parent.offsetHeight; - - window.chart.resize(); - }); -}); - -// File: styles.css -@import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap'); - -* { - font-family:"Poppins", sans-serif; -} - -body { - margin: 0; - color: #333; - background-color: #FFEDFF; - - } - - .topnav { - overflow: hidden; - background-color: #333; - } - - .topnav a { - float: right; - display: block; - color: #f2f2f2; - text-align: center; - padding: 14px 16px; - text-decoration: none; - font-size: 17px; - } - - .topnav a:hover { - background-color: #ddd; - color: black; - font-weight: bold; - } - - .topnav a:active { - background-color: #4CAF50; - color: white; - } - - .logo { - float: left; - display: block; - color: #f2f2f2; - text-align: center; - padding: 14px 16px; - text-decoration: none; - font-size: 17px; - } - - .content { - display: flex; - justify-content: space-between; - align-items: center; - } - - .dashboard { - margin: 0; - } - - .box-container { - display: inline-flex; - gap: 50px; - } - - .box { - width: 150px; - height: auto; - padding: 10px; - border-radius: 15px; - background-color: white; - text-align: center; - } - - .box h3, .box p { - font-size: 14px; - } - - .separator { - text-align: left; - margin: 20px 0; - } - - .separator span { - background: transparent; - padding: 0 10px; - } - - .separator hr { - border: none; - border-top: 1px solid black; - } - - .page-wrapper { - margin: 50px; -} - -.filter-options { - display: flex; - flex-direction: column; - align-items: flex-start; - width: 20%; - margin-right: 20px; - display: flex; - border-right: 1px solid black; - } - - .filter-options label { - display: flex; - align-items: center; - margin-bottom: 10px; - } - - .filter-options input[type="checkbox"] { - margin-right: 5px; - } - - .filter-options h3 { - margin-bottom: 10px; -} - -.product-category, -.month, -.day { - margin-bottom: 20px; -} - -.product-category h3, -.month h3, -.day h3 { - font-weight: bold; -} - -.line-separator { - border: none; - border-top: 1px solid black; - width: calc(100% - 20px); - margin: 10px 0; -} - -.data { - flex-grow: 1; - display: flex; - flex-direction: column; - align-items: stretch; - border: solid 1px black; -} - -.trendDay { - flex-grow: 1; - position: relative; - border: solid 1px black; - min-height: 500px; -} - -.transactionAndRevenue { - display: flex; - flex-direction: row; -} - -.totalTransaction, .totalRevenue { - flex-grow: 1; - position: relative; - width: 50%; - border: solid 1px black; - -} - -#totalTransaction, #totalRevenue { - max-height: 500px; -} - -.index-container { - display: flex; - align-items: flex-start; - flex-direction: row; -} - -.our-members { - width: 100%; - height: fit-content; - border: 2px solid black; - text-align: left; - line-height: 40px; - padding-left: 20px; - box-sizing: border-box; -} - -.team-members { - width: 100%; - display: flex; - flex-wrap: wrap; - justify-content: space-between; - border: 2px solid black; - box-sizing: border-box; - padding: 3%; -} - - -.member { - flex-basis: calc(30% - 5%); - max-width: calc(30% - 5%); - text-align: center; - margin-bottom: 20px; -} - -.member img { - width: 150px; - height: 150px; - border-radius: 50%; - margin-bottom: 10px; -} - -.invisible-separator { - height: 10px; - border-top: 10px solid transparent; - border-bottom: 10px solid transparent; -} - - -header { - background-color: #fff; - color: #333; - box-shadow: 0 10px 10px rgba(0, 0, 0, 0.25); - padding: 20px; - text-align: center; -} - -nav { - display: flex; - justify-content: space-between; -} - -nav a { - text-decoration: none; -} - -nav a.active { - text-decoration: underline; - font-weight: bold; -} - - -a:hover { - text-decoration: underline; -} - -.logo-header { - display: flex; - gap: 30px; -} - -.section-name{ - text-align: left; - line-height:0px; -} - -.menu { - display: flex; - gap: 30px; - margin: auto 0; -} - -footer { - background-color: #fff; - color: #333; - box-shadow: 0 -3px 15px 3px rgba(0, 0, 0, 0.25); - padding: 20px; - text-align: center; - margin-top: 20px; - display: flex; - justify-content: space-between; -} - -.contact { - display: flex; - flex-wrap: wrap; - justify-content: space-between; -} - -.profile-pic { - border-radius: 50%; - width: 150px; - height: 150px; - margin-right: 20px; - object-fit: cover; -} - -.member-contact { - display: flex; - align-items: center; - width: 45%; - margin-bottom: 10%; - padding: 10px; - margin-bottom: 40px; -} - -.contact-info { - display: flex; - flex-direction: column; - justify-content: center; - border-top: 1px solid black; - flex-grow: 1; -} - -.contact-info p { - margin: 5px 0; /* Adjust margin between each paragraph */ -} - - -@media (max-width: 768px) { - .member-contact { - width: 100%; - margin-bottom: 5%; - } -} - - diff --git a/get_code_context.sh b/get_code_context.sh deleted file mode 100644 index 92e53c6..0000000 --- a/get_code_context.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash - -# This works for next.js projects currently -# Put this in your root folder of your project -# run the command chmod +x get_code_context.sh -# then run ./get_code_context.sh - -# Use the current directory as the project directory -project_dir=$(pwd) - -# Use a fixed name for the output file in the current directory -output_file="${project_dir}/code_context.txt" - -# Check if the output file exists and remove it if it does -if [ -f "$output_file" ]; then - rm "$output_file" -fi - -# List of directories to look for -directories=("data_schema" "script" ) - -# List of directories to ignore -ignore_dirs=("data" "image" ".vscode" "node_modules") - -# List of files to ignore -ignore_files=("*.ico" "*.png" "*.jpg" "*.jpeg" "*.gif" "*.svg" "README.md" "tempCodeRunnerFile.js" ".gitattributes" "get_code_context.sh") - -# Recursive function to read files and append their content -read_files() { - for entry in "$1"/* - do - if [ -d "$entry" ]; then - # If entry is a directory, check if it should be ignored - should_ignore=false - for ignore_pattern in "${ignore_dirs[@]}"; do - if [[ "$entry" == *"$ignore_pattern"* ]]; then - should_ignore=true - break - fi - done - - # If the directory should not be ignored, call this function recursively - if ! $should_ignore; then - read_files "$entry" - fi - elif [ -f "$entry" ]; then - # Check if the file should be ignored - should_ignore=false - for ignore_pattern in "${ignore_files[@]}"; do - if [[ "$entry" == *"$ignore_pattern"* ]]; then - should_ignore=true - break - fi - done - - # If the file should not be ignored, append its relative path and content to the output file - if ! $should_ignore; then - relative_path=${entry#"$project_dir/"} - echo "// File: $relative_path" >> "$output_file" - cat "$entry" >> "$output_file" - echo "" >> "$output_file" - fi - fi - done -} - -# Call the recursive function for each specified directory in the project directory -for dir in "${directories[@]}"; do - if [ -d "${project_dir}/${dir}" ]; then - read_files "${project_dir}/${dir}" - fi -done - -# Call the recursive function for the root directory -read_files "${project_dir}" diff --git a/index.html b/index.html index 5d75734..e6d9347 100644 --- a/index.html +++ b/index.html @@ -134,7 +134,16 @@

Trend

Daily Transaction per Product Type

- + +

Leading Store for Each Product Type

+ + + + + + + +
Product TypeStoreTransactionsRevenue
@@ -146,7 +155,7 @@

Total Revenue per Product Type

-
+

Distribution

@@ -167,8 +176,11 @@

Product Variety on Transaction

Product Price Influence on Purchasing Behaviour

-
-
+
+
@@ -181,7 +193,11 @@

Product Price Influence on Purchasing Behaviour

- + + diff --git a/script/filter.js b/script/filter.js index 854a399..6c9f749 100644 --- a/script/filter.js +++ b/script/filter.js @@ -1,19 +1,21 @@ -// filter.js export function filterData(originalData) { - return originalData.filter((item) => { - if (!document.querySelector(`input[value="${item.dayOfWeek}"]`).checked) { - return false; - } - if (!document.querySelector(`input[value="${item.monthOfYear}"]`).checked) { - return false; - } - if (!document.querySelector(`input[value="${item.productCategory}"]`).checked) { - return false; - } - if (!document.querySelector(`input[value="${item.storeLocation}"]`).checked) { - return false; - } - return true; + return originalData.filter((item) => { + const dayOfWeekChecked = document.querySelector(`input[value="${item.dayOfWeek}"]`).checked; + const monthOfYearChecked = document.querySelector(`input[value="${item.monthOfYear}"]`).checked; + const productCategoryChecked = document.querySelector(`input[value="${item.productCategory}"]`).checked; + const storeLocationChecked = document.querySelector(`input[value="${item.storeLocation}"]`).checked; + + return dayOfWeekChecked && monthOfYearChecked && productCategoryChecked && storeLocationChecked; + }); +} + +export function addCheckboxEventListeners(updateDataDisplay, originalData) { + document + .querySelectorAll('input[type="checkbox"]') + .forEach((checkbox) => { + checkbox.addEventListener("change", () => { + const filteredData = filterData(originalData); + updateDataDisplay(filteredData); + }); }); - } - \ No newline at end of file +} diff --git a/script/leadingStore.js b/script/leadingStore.js new file mode 100644 index 0000000..b8629c5 --- /dev/null +++ b/script/leadingStore.js @@ -0,0 +1,63 @@ +import { filterData, addCheckboxEventListeners } from './filter.js'; + +window.addEventListener('load', initialize); + +function initialize() { + fetchData("data/trend_data.json") + .then((originalData) => { + const updateDataDisplay = createDataDisplayUpdater(originalData); + updateDataDisplay(originalData); + addCheckboxEventListeners(updateDataDisplay, originalData); + }) + .catch((error) => console.error("Error:", error)); +} + +function fetchData(url) { + return fetch(url).then((response) => response.json()); +} + +function createDataDisplayUpdater(originalData) { + return function updateDataDisplay(data) { + const filteredData = filterData(data); + processAndDisplayData(filteredData); + }; +} + +function processAndDisplayData(data) { + const table = document.getElementById("storeTable"); + table.innerHTML = ''; // Clear existing table rows + + const groupedData = data.reduce((acc, curr) => { + const key = `${curr.productCategory}-${curr.productType}-${curr.storeLocation}`; + if (!acc[key]) { + acc[key] = { + productCategory: curr.productCategory, + productType: curr.productType, + storeLocation: curr.storeLocation, + totalQty: 0, + totalRevenue: 0, + }; + } + acc[key].totalQty += curr.totalQty; + acc[key].totalRevenue += curr.totalRevenue; + return acc; + }, {}); + + const sortedData = Object.values(groupedData).sort((a, b) => { + if (a.productCategory < b.productCategory) return -1; + if (a.productCategory > b.productCategory) return 1; + if (a.productType < b.productType) return -1; + if (a.productType > b.productType) return 1; + return b.totalRevenue - a.totalRevenue; + }); + + sortedData.forEach((item) => { + const row = ` + ${item.productType} + ${item.storeLocation} + ${item.totalQty} + ${item.totalRevenue} + `; + table.innerHTML += row; + }); +} diff --git a/script/priceInfluencePurchasingBehaviour.js b/script/priceInfluencePurchasingBehaviour.js index 19f78f6..f444ef5 100644 --- a/script/priceInfluencePurchasingBehaviour.js +++ b/script/priceInfluencePurchasingBehaviour.js @@ -1,4 +1,4 @@ -import { filterData } from './filter.js'; +import { filterData, addCheckboxEventListeners } from './filter.js'; import productColors from "../colors.js"; window.addEventListener('load', function() { @@ -104,16 +104,7 @@ window.addEventListener('load', function() { } updateChart(originalData); - - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - const filteredData = filterData(originalData); - - updateChart(filteredData); - }); - }); + addCheckboxEventListeners(updateChart, originalData); }) .catch((error) => console.error("Error:", error)); }); diff --git a/script/productVarietyRevenue.js b/script/productVarietyRevenue.js index 224e453..a9adbba 100644 --- a/script/productVarietyRevenue.js +++ b/script/productVarietyRevenue.js @@ -1,4 +1,4 @@ -import { filterData } from './filter.js'; +import { filterData, addCheckboxEventListeners } from './filter.js'; import productColors from "../colors.js"; window.addEventListener('load', function() { @@ -115,15 +115,7 @@ window.addEventListener('load', function() { updateChart(originalData); - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - const filteredData = filterData(originalData); - - updateChart(filteredData); - }); - }); + addCheckboxEventListeners(updateChart, originalData); }) .catch((error) => console.error("Error:", error)); diff --git a/script/productVarietyTransaction.js b/script/productVarietyTransaction.js index 99f65e0..6e6a337 100644 --- a/script/productVarietyTransaction.js +++ b/script/productVarietyTransaction.js @@ -1,4 +1,4 @@ -import { filterData } from './filter.js'; +import { filterData, addCheckboxEventListeners } from './filter.js'; import productColors from "../colors.js"; window.addEventListener('load', function() { @@ -115,15 +115,7 @@ window.addEventListener('load', function() { updateChart(originalData); - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - const filteredData = filterData(originalData); - - updateChart(filteredData); - }); - }); + addCheckboxEventListeners(updateChart, originalData); }) .catch((error) => console.error("Error:", error)); }); diff --git a/script/totalRevenue.js b/script/totalRevenue.js index fd707d9..12adea5 100644 --- a/script/totalRevenue.js +++ b/script/totalRevenue.js @@ -1,4 +1,4 @@ -import { filterData } from './filter.js'; +import { filterData, addCheckboxEventListeners } from './filter.js'; import productColors from "../colors.js"; window.addEventListener('load', function() { @@ -98,15 +98,7 @@ window.addEventListener('load', function() { updateChart(originalData); - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - const filteredData = filterData(originalData); - - updateChart(filteredData); - }); - }); + addCheckboxEventListeners(updateChart, originalData); }) .catch((error) => console.error("Error:", error)); }); diff --git a/script/totalTransaction.js b/script/totalTransaction.js index fac72fe..3b5ae09 100644 --- a/script/totalTransaction.js +++ b/script/totalTransaction.js @@ -1,4 +1,4 @@ -import { filterData } from './filter.js'; +import { filterData, addCheckboxEventListeners } from './filter.js'; import productColors from "../colors.js"; window.addEventListener('load', function() { @@ -99,17 +99,7 @@ window.addEventListener('load', function() { // Initial chart creation updateChart(originalData); - // Add event listener to each checkbox - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - // Filter out the data that matches the unchecked property - const filteredData = filterData(originalData); - - updateChart(filteredData); - }); - }); + addCheckboxEventListeners(updateChart, originalData); }) .catch((error) => console.error("Error:", error)); }); diff --git a/script/trendDay.js b/script/trendDay.js index b118244..25071ba 100644 --- a/script/trendDay.js +++ b/script/trendDay.js @@ -1,5 +1,5 @@ import productColors from "../colors.js"; -import { filterData } from './filter.js'; +import { filterData, addCheckboxEventListeners } from './filter.js'; window.addEventListener('load', function() { const ctx = document.getElementById("trendDay").getContext("2d"); @@ -101,17 +101,7 @@ window.addEventListener('load', function() { updateChart(originalData); // Add event listener to each checkbox - document - .querySelectorAll('input[type="checkbox"]') - .forEach((checkbox) => { - checkbox.addEventListener("change", () => { - // Filter out the data that matches the unchecked property - const filteredData = filterData(originalData); - - // Update the chart - updateChart(filteredData); - }); - }); + addCheckboxEventListeners(updateChart, originalData); }) .catch((error) => console.error("Error:", error)); diff --git a/styles.css b/styles.css index 1ba34f0..f3d8c1b 100644 --- a/styles.css +++ b/styles.css @@ -209,9 +209,9 @@ body { } .trend h3 { - display: block; /* This will make the h3 element take up the full width of its parent, forcing it to a new line */ - margin-top: 10px; /* Adjust this to change the space above the h3 element */ - font-size: 20px; /* Adjust this to change the font size of the h3 element */ + display: block; + margin-top: 10px; + font-size: 20px; } @@ -219,6 +219,8 @@ body { flex-grow: 1; position: relative; max-height: 500px; + flex-direction: column; + margin-right: 20px; } .trend { @@ -441,5 +443,17 @@ footer { margin-right: 5px; } +/* style.css */ +#storeTable { + display: block; + height: 300px; + overflow: auto; +} +#storeTable tr { + display: table; + width: 100%; + table-layout: fixed; +} + diff --git a/tempCodeRunnerFile.js b/tempCodeRunnerFile.js deleted file mode 100644 index 72216c4..0000000 --- a/tempCodeRunnerFile.js +++ /dev/null @@ -1,14 +0,0 @@ -const fs = require('fs'); -const Papa = require('papaparse'); - -fs.readFile('data/transaction.csv', 'utf8', function(err, data) { - if (err) { - return console.log(err); - } - Papa.parse(data, { - header: true, - complete: function(results) { - console.log(results.data); - } - }); -});