refactor: setup prettier as formatter for the whole monorepo (#26)

This commit is contained in:
Marco Pasqualetti
2026-01-20 18:38:20 +01:00
committed by GitHub
parent 8c065b473f
commit 74b314ab0f
91 changed files with 1301 additions and 1289 deletions
+27 -27
View File
@@ -1,33 +1,33 @@
{
"name": "GitHub Readme Stats Dev",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/node:1": { "version": "22" }
},
"forwardPorts": [3000],
"portsAttributes": {
"3000": { "label": "HTTP" }
},
"appPort": [],
"name": "GitHub Readme Stats Dev",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/node:1": { "version": "22" }
},
"forwardPorts": [3000],
"portsAttributes": {
"3000": { "label": "HTTP" }
},
"appPort": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "npm install -g vercel",
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "npm install -g vercel",
// Use 'postStartCommand' to run commands after the container is started.
"postStartCommand": "hostname dev && npm install",
// Use 'postStartCommand' to run commands after the container is started.
"postStartCommand": "hostname dev && npm install",
// Configure tool-specific properties.
"customizations": {
"vscode": {
"extensions": [
"yzhang.markdown-all-in-one",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"github.vscode-github-actions"
]
}
},
// Configure tool-specific properties.
"customizations": {
"vscode": {
"extensions": [
"yzhang.markdown-all-in-one",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"github.vscode-github-actions"
]
}
},
"remoteUser": "root",
"privileged": true
"remoteUser": "root",
"privileged": true
}
+2 -4
View File
@@ -37,11 +37,9 @@ jobs:
close_comment:
Closing this issue because it appears to be empty. Please update the
issue for it to be reopened.
open_comment:
Reopening this issue because the author provided more information.
open_comment: Reopening this issue because the author provided more information.
check_templates: true
template_close_comment:
Closing this issue since the issue template was not filled in.
Please provide us with more information to have this issue reopened.
template_open_comment:
Reopening this issue because the author provided more information.
template_open_comment: Reopening this issue because the author provided more information.
+1 -2
View File
@@ -59,8 +59,7 @@ jobs:
branch: "update_langs/patch"
delete-branch: true
title: Update languages JSON
body:
"The
body: "The
[update-langs](https://github.com/anuraghazra/github-readme-stats/actions/workflows/update-langs.yaml)
action found new/updated languages in the [upstream languages JSON
file](https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml)."
-1
View File
@@ -1 +0,0 @@
22
-5
View File
@@ -1,5 +0,0 @@
node_modules
*.json
*.md
coverage
.vercel
-6
View File
@@ -1,6 +0,0 @@
{
"trailingComma": "all",
"useTabs": false,
"endOfLine": "auto",
"proseWrap": "always"
}
-4
View File
@@ -1,7 +1,5 @@
.devcontainer
.github
.husky
.vscode
benchmarks
coverage
scripts
@@ -10,6 +8,4 @@ tests
**/*.md
**/*.svg
.eslintrc.json
.prettierignore
.pretterrc.json
codecov.yml
-9
View File
@@ -1,9 +0,0 @@
{
"recommendations": [
"yzhang.markdown-all-in-one",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"ms-azuretools.vscode-containers",
"github.vscode-github-actions"
]
}
-8
View File
@@ -1,8 +0,0 @@
{
"markdown.extension.toc.levels": "1..3",
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": {
"editor.tabSize": 2
}
}
@@ -2,4 +2,4 @@
"expiration": 39600,
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
"passQuery": true
}
}
@@ -2,4 +2,4 @@
"expiration": 39600,
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
"passQuery": true
}
}
@@ -2,4 +2,4 @@
"expiration": 39600,
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
"passQuery": true
}
}
@@ -2,4 +2,4 @@
"expiration": 39600,
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
"passQuery": true
}
}
@@ -2,4 +2,4 @@
"expiration": 39600,
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
"passQuery": true
}
}
+8 -5
View File
@@ -53,10 +53,10 @@ export default async (req, res) => {
},
);
} catch (err) {
logger.error(err);
res.statusCode = 500;
res.send("Failed to delete GitHub authorization with private access");
return;
logger.error(err);
res.statusCode = 500;
res.send("Failed to delete GitHub authorization with private access");
return;
}
await deleteUser(user_key);
@@ -68,6 +68,9 @@ export default async (req, res) => {
}).toString();
res.statusCode = 302;
res.setHeader("Location", `https://github.com/login/oauth/authorize?${params}`);
res.setHeader(
"Location",
`https://github.com/login/oauth/authorize?${params}`,
);
res.end();
};
+15 -14
View File
@@ -94,19 +94,18 @@ export default async (req, res) => {
(owner && !safePattern.test(owner))
) {
return res.send(
renderError(
{
message: "Something went wrong",
secondaryMessage: "Username, repository or owner contains unsafe characters",
renderOptions: {
title_color,
text_color,
bg_color,
border_color,
theme,
},
renderError({
message: "Something went wrong",
secondaryMessage:
"Username, repository or owner contains unsafe characters",
renderOptions: {
title_color,
text_color,
bg_color,
border_color,
theme,
},
),
}),
);
}
@@ -124,7 +123,7 @@ export default async (req, res) => {
parseBoolean(include_all_commits),
parseArray(exclude_repo),
showStats.includes("prs_merged") ||
showStats.includes("prs_merged_percentage"),
showStats.includes("prs_merged_percentage"),
showStats.includes("discussions_started"),
showStats.includes("discussions_answered"),
parseInt(commits_year, 10),
@@ -147,7 +146,9 @@ export default async (req, res) => {
setCacheHeaders(res, cacheSeconds);
return res.send(
renderStatsCard(stats, {
renderStatsCard(
stats,
{
hide: parseArray(hide),
show_icons: parseBoolean(show_icons),
hide_title: parseBoolean(hide_title),
+1 -1
View File
@@ -18,7 +18,7 @@ export default async (req, res) => {
res.send({
privateAccess: result.privateAccess,
token: result.token
token: result.token,
});
} catch (err) {
logger.error(err);
-2
View File
@@ -27,8 +27,6 @@
"preview-theme": "node scripts/preview-theme",
"close-stale-theme-prs": "node scripts/close-stale-theme-prs",
"generate-langs-json": "node scripts/generate-langs-json",
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "npx eslint --max-warnings 0 \"./src/**/*.js\" \"./scripts/**/*.js\" \"./tests/**/*.js\" \"./api-renamed/**/*.js\" \"./themes/**/*.js\"",
"bench": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.bench.config.js"
},
-1
View File
@@ -14,7 +14,6 @@ import { icons } from "../common/icons.js";
import languageColors from "../common/languageColors.json" with { type: "json" };
import { parseEmojis } from "../common/ops.js";
const ICON_SIZE = 16;
const CARD_DEFAULT_WIDTH = 400;
const HEADER_MAX_LENGTH = 35;
+2 -1
View File
@@ -378,7 +378,8 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
// @ts-ignore
progressBarColor: titleColor,
// @ts-ignore
progressBarBackgroundColor: textColor === titleColor ? bgColor : textColor,
progressBarBackgroundColor:
textColor === titleColor ? bgColor : textColor,
hideProgress: hide_progress,
progressBarWidth: normalizedWidth - TOTAL_TEXT_WIDTH,
});
+2 -1
View File
@@ -83,7 +83,8 @@ const getCardColors = ({
theme,
}) => {
const defaultTheme = themes["default"];
const isThemeProvided = theme !== null && theme !== undefined && theme in themes;
const isThemeProvided =
theme !== null && theme !== undefined && theme in themes;
// @ts-ignore
const selectedTheme = isThemeProvided ? themes[theme] : defaultTheme;
+2 -2
View File
@@ -215,7 +215,7 @@ export async function getUserAccessByKey(userKey) {
}
return {
token: rows[0].access_token,
privateAccess: rows[0].private_access
privateAccess: rows[0].private_access,
};
} catch (err) {
if (err.code === "42P01") {
@@ -250,7 +250,7 @@ export async function getUserAccessByName(userName) {
}
return {
token: rows[0].access_token,
privateAccess: rows[0].private_access
privateAccess: rows[0].private_access,
};
} catch (err) {
if (err.code === "42P01") {
File diff suppressed because it is too large Load Diff
+2 -8
View File
@@ -5,10 +5,7 @@ import * as dotenv from "dotenv";
import githubUsernameRegex from "github-username-regex";
import { calculateRank } from "../calculateRank.js";
import { retryer } from "../common/retryer.js";
import {
buildSearchFilter,
parseOwnerAffiliations,
} from "../common/ops.js";
import { buildSearchFilter, parseOwnerAffiliations } from "../common/ops.js";
import { logger } from "../common/log.js";
import { excludeRepositories } from "../common/envs.js";
import { CustomError, MissingParamError } from "../common/error.js";
@@ -191,10 +188,7 @@ const fetchTotalItems = (variables, token) => {
`https://api.github.com/search/` +
variables.type +
`?per_page=1&q=` +
buildSearchFilter(variables.repo, variables.owner).replaceAll(
" ",
"+",
) +
buildSearchFilter(variables.repo, variables.owner).replaceAll(" ", "+") +
variables.filter,
headers: {
"Content-Type": "application/json",
+4 -1
View File
@@ -73,7 +73,10 @@ const fetchTopLanguages = async (
}
ownerAffiliations = parseOwnerAffiliations(ownerAffiliations);
const res = await retryer(fetcher, username, { login: username, ownerAffiliations });
const res = await retryer(fetcher, username, {
login: username,
ownerAffiliations,
});
if (res.data.errors) {
logger.error(res.data.errors);
+2 -1
View File
@@ -1,4 +1,5 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"buildCommand": "pnpm install && ../../vercel-preparation.sh",
"redirects": [
{
@@ -12,4 +13,4 @@
"destination": "https://gse-frontend-preview.vercel.app/:match*"
}
]
}
}
+16 -16
View File
@@ -3,36 +3,36 @@ module.exports = {
browser: true,
es6: true,
},
extends: ['plugin:prettier/recommended'],
extends: ["plugin:prettier/recommended"],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2020,
sourceType: 'module',
sourceType: "module",
},
plugins: ['prettier'],
plugins: ["prettier"],
rules: {
'react/jsx-filename-extension': 'off',
'react/forbid-prop-types': 'off',
'react/destructuring-assignment': 'off',
'import/prefer-default-export': 'off',
'react/function-component-definition': 'off',
'react/no-unstable-nested-components': 'off',
'no-console': 'off',
radix: 'off',
'prettier/prettier': [
'error',
"react/jsx-filename-extension": "off",
"react/forbid-prop-types": "off",
"react/destructuring-assignment": "off",
"import/prefer-default-export": "off",
"react/function-component-definition": "off",
"react/no-unstable-nested-components": "off",
"no-console": "off",
radix: "off",
"prettier/prettier": [
"error",
{
endOfLine: 'auto',
endOfLine: "auto",
},
],
},
overrides: [
{
files: ['./src/backend/**/*'],
files: ["./src/backend/**/*"],
rules: {
'prettier/prettier': 'off',
"prettier/prettier": "off",
},
},
],
-23
View File
@@ -1,23 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
-7
View File
@@ -1,7 +0,0 @@
module.exports = {
semi: true,
trailingComma: 'all',
singleQuote: true,
printWidth: 80,
tabWidth: 2,
};
+20 -19
View File
@@ -1,38 +1,39 @@
const path = require('path');
const webpack = require('webpack');
const path = require("path");
const webpack = require("webpack");
module.exports = {
webpack: {
alias: {
dotenv: path.resolve(__dirname, 'src/dotenv-browser-stub.js'),
'../src/fetchers/wakatime.js': path.resolve(__dirname, 'src/wakatime-override.js'),
dotenv: path.resolve(__dirname, "src/dotenv-browser-stub.js"),
"../src/fetchers/wakatime.js": path.resolve(
__dirname,
"src/wakatime-override.js",
),
},
configure: (webpackConfig) => {
webpackConfig.resolve.fallback = {
...webpackConfig.resolve.fallback,
pg: false,
path: require.resolve('path-browserify'),
querystring: require.resolve('querystring-es3'),
url: require.resolve('url/'),
http: require.resolve('stream-http'),
util: require.resolve('util/'),
stream: require.resolve('stream-browserify'),
buffer: require.resolve('buffer/'),
path: require.resolve("path-browserify"),
querystring: require.resolve("querystring-es3"),
url: require.resolve("url/"),
http: require.resolve("stream-http"),
util: require.resolve("util/"),
stream: require.resolve("stream-browserify"),
buffer: require.resolve("buffer/"),
fs: false,
net: false,
};
// remove the DefinePlugin, which (only) replaces process.env in code
webpackConfig.plugins = webpackConfig.plugins.filter(
function (plugin) {
let isDefinePlugin = plugin instanceof webpack.DefinePlugin;
if (isDefinePlugin) {
console.log("filtered plugin:", plugin);
}
return !isDefinePlugin;
webpackConfig.plugins = webpackConfig.plugins.filter(function (plugin) {
let isDefinePlugin = plugin instanceof webpack.DefinePlugin;
if (isDefinePlugin) {
console.log("filtered plugin:", plugin);
}
);
return !isDefinePlugin;
});
return webpackConfig;
},
+5 -2
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
@@ -13,7 +13,10 @@
/>
<meta property="og:type" content="website" />
<meta property="og:url" content="https://github-stats-extended.vercel.app/frontend" />
<meta
property="og:url"
content="https://github-stats-extended.vercel.app/frontend"
/>
<meta property="og:title" content="GitHub Stats Extended" />
<meta
property="og:description"
+1 -1
View File
@@ -1,3 +1,3 @@
import { authenticate, getUserMetadata, deleteAccount } from './user';
import { authenticate, getUserMetadata, deleteAccount } from "./user";
export { authenticate, getUserMetadata, deleteAccount };
+4 -4
View File
@@ -1,6 +1,6 @@
import axios from 'axios';
import axios from "axios";
import { HOST } from '../constants';
import { HOST } from "../constants";
const authenticate = async (code, privateAccess, userKey) => {
try {
@@ -16,7 +16,7 @@ const authenticate = async (code, privateAccess, userKey) => {
return userId;
} catch (error) {
console.error(error);
return '';
return "";
}
};
@@ -38,7 +38,7 @@ const deleteAccount = async (userId, userKey) => {
return result.data; // no decorator
} catch (error) {
console.error(error);
return '';
return "";
}
};
+38 -38
View File
@@ -1,27 +1,27 @@
import axios, { getAdapter } from 'axios';
import { setupCache } from 'axios-cache-interceptor';
import { HOST } from './constants';
import axios, { getAdapter } from "axios";
import { setupCache } from "axios-cache-interceptor";
import { HOST } from "./constants";
import additionalUserStars from './mockData/additional_user_stars.json' with { type: 'json' };
import commentedIssues from './mockData/commented_issues.json' with { type: 'json' };
import commentedPrs from './mockData/commented_prs.json' with { type: 'json' };
import commits from './mockData/commits.json' with { type: 'json' };
import gist_graphql from './mockData/gist-graphql.json' with { type: 'json' };
import gist_rest from './mockData/gist-rest.json' with { type: 'json' };
import repository from './mockData/repository.json' with { type: 'json' };
import reviewedPrs from './mockData/reviewed_prs.json' with { type: 'json' };
import topLanguages from './mockData/top_languages.json' with { type: 'json' };
import userStats from './mockData/user_stats.json' with { type: 'json' };
import wakatimeProxy from './mockData/wakatime_proxy.json' with { type: 'json' };
import additionalUserStars from "./mockData/additional_user_stars.json" with { type: "json" };
import commentedIssues from "./mockData/commented_issues.json" with { type: "json" };
import commentedPrs from "./mockData/commented_prs.json" with { type: "json" };
import commits from "./mockData/commits.json" with { type: "json" };
import gist_graphql from "./mockData/gist-graphql.json" with { type: "json" };
import gist_rest from "./mockData/gist-rest.json" with { type: "json" };
import repository from "./mockData/repository.json" with { type: "json" };
import reviewedPrs from "./mockData/reviewed_prs.json" with { type: "json" };
import topLanguages from "./mockData/top_languages.json" with { type: "json" };
import userStats from "./mockData/user_stats.json" with { type: "json" };
import wakatimeProxy from "./mockData/wakatime_proxy.json" with { type: "json" };
const cachedAxios = setupCache(axios, {
// Cache for 30 minutes
ttl: 30 * 60 * 1000,
interpretHeader: false,
cacheTakeover: false,
methods: ['get', 'post'],
methods: ["get", "post"],
cachePredicate: {
allowUrls: ['api.github.com', HOST],
allowUrls: ["api.github.com", HOST],
},
});
@@ -36,7 +36,7 @@ function createMockResponse(data, config) {
return Promise.resolve({
data,
status: 200,
statusText: 'OK',
statusText: "OK",
headers: {},
request: {},
config,
@@ -61,62 +61,62 @@ axios.defaults.adapter = async (config) => {
const params = config.data ? JSON.parse(config.data) : {};
if (
config.url === 'https://api.github.com/graphql' &&
config.url === "https://api.github.com/graphql" &&
params.query?.includes(
'query userInfo($login: String!, $after: String, $includeMergedPullRequests:',
"query userInfo($login: String!, $after: String, $includeMergedPullRequests:",
) &&
params.variables?.login === 'anuraghazra'
params.variables?.login === "anuraghazra"
) {
return createMockResponse(userStats, config);
}
if (
config.url === 'https://api.github.com/graphql' &&
config.url === "https://api.github.com/graphql" &&
params.query?.includes(
'query userInfo($login: String!, $after: String, $ownerAffiliations:',
"query userInfo($login: String!, $after: String, $ownerAffiliations:",
) &&
params.variables?.login === 'anuraghazra'
params.variables?.login === "anuraghazra"
) {
return createMockResponse(additionalUserStars, config);
}
if (
config.url === 'https://api.github.com/graphql' &&
config.url === "https://api.github.com/graphql" &&
params.query?.includes(
'query userInfo($login: String!, $ownerAffiliations:',
"query userInfo($login: String!, $ownerAffiliations:",
) &&
params.variables?.login === 'anuraghazra'
params.variables?.login === "anuraghazra"
) {
return createMockResponse(topLanguages, config);
}
if (
config.url === 'https://api.github.com/graphql' &&
params.query?.includes('fragment RepoInfo on Repository {') &&
params.variables?.login === 'anuraghazra' &&
params.variables?.repo === 'github-readme-stats'
config.url === "https://api.github.com/graphql" &&
params.query?.includes("fragment RepoInfo on Repository {") &&
params.variables?.login === "anuraghazra" &&
params.variables?.repo === "github-readme-stats"
) {
return createMockResponse(repository, config);
}
if (
config.url === 'https://api.github.com/graphql' &&
params.query?.includes('query gistInfo(') &&
params.variables?.gistName === 'bbfce31e0217a3689c8d961a356cb10d'
config.url === "https://api.github.com/graphql" &&
params.query?.includes("query gistInfo(") &&
params.variables?.gistName === "bbfce31e0217a3689c8d961a356cb10d"
) {
return createMockResponse(gist_graphql, config);
}
switch (config.url) {
case 'https://api.github.com/gists/bbfce31e0217a3689c8d961a356cb10d':
case "https://api.github.com/gists/bbfce31e0217a3689c8d961a356cb10d":
return createMockResponse(gist_rest, config);
case 'https://api.github.com/search/commits?per_page=1&q=author:anuraghazra':
case "https://api.github.com/search/commits?per_page=1&q=author:anuraghazra":
return createMockResponse(commits, config);
case 'https://api.github.com/search/issues?per_page=1&q=commenter:anuraghazra+-author:anuraghazra+type:pr':
case "https://api.github.com/search/issues?per_page=1&q=commenter:anuraghazra+-author:anuraghazra+type:pr":
return createMockResponse(commentedPrs, config);
case 'https://api.github.com/search/issues?per_page=1&q=reviewed-by:anuraghazra+-author:anuraghazra+type:pr':
case "https://api.github.com/search/issues?per_page=1&q=reviewed-by:anuraghazra+-author:anuraghazra+type:pr":
return createMockResponse(reviewedPrs, config);
case 'https://api.github.com/search/issues?per_page=1&q=commenter:anuraghazra+-author:anuraghazra+type:issue':
case "https://api.github.com/search/issues?per_page=1&q=commenter:anuraghazra+-author:anuraghazra+type:issue":
return createMockResponse(commentedIssues, config);
case `https://${HOST}/api/wakatime-proxy?username=ffflabs`:
return createMockResponse(wakatimeProxy, config);
+12 -12
View File
@@ -1,11 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import SVG from './SVG';
import { classnames } from '../../utils';
import { HOST } from '../../constants';
import SVG from "./SVG";
import { classnames } from "../../utils";
import { HOST } from "../../constants";
export const Image = ({ imageSrc, stage, compact, extraClasses = '' }) => {
export const Image = ({ imageSrc, stage, compact, extraClasses = "" }) => {
const fullImageSrc = `https://${HOST}/api${imageSrc}&client=wizard`;
return (
@@ -29,7 +29,7 @@ Image.propTypes = {
Image.defaultProps = {
compact: false,
extraClasses: '',
extraClasses: "",
};
export const Card = ({
@@ -44,11 +44,11 @@ export const Card = ({
return (
<div
className={classnames(
'p-6 rounded border-2',
fixedSize ? 'h-[370px] w-[510px]' : '',
"p-6 rounded border-2",
fixedSize ? "h-[370px] w-[510px]" : "",
selected
? 'border-blue-500 bg-blue-50'
: 'border-gray-200 bg-white hover:bg-gray-50',
? "border-blue-500 bg-blue-50"
: "border-gray-200 bg-white hover:bg-gray-50",
)}
>
<h2 className="text-xl font-medium title-font text-gray-900">{title}</h2>
@@ -56,7 +56,7 @@ export const Card = ({
<Image
imageSrc={imageSrc}
compact={compact}
extraClasses={fixedSize ? 'flex justify-center' : ''}
extraClasses={fixedSize ? "flex justify-center" : ""}
stage={stage}
/>
</div>
+17 -17
View File
@@ -1,20 +1,20 @@
/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable react/no-danger */
import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import React, { useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import Skeleton from 'react-loading-skeleton';
import 'react-loading-skeleton/dist/skeleton.css';
import Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css";
import { createMockReq, createMockRes } from '../../mock-http';
import { default as router } from '../../backend/.vercel/output/functions/api.func/router.js';
import { setShouldMock } from '../../axios-override';
import { createMockReq, createMockRes } from "../../mock-http";
import { default as router } from "../../backend/.vercel/output/functions/api.func/router.js";
import { setShouldMock } from "../../axios-override";
import {
useIsAuthenticated,
useUserToken,
} from '../../redux/selectors/userSelectors';
import axios from 'axios';
} from "../../redux/selectors/userSelectors";
import axios from "axios";
const SvgInline = (props) => {
const [svg, setSvg] = useState(null);
@@ -40,7 +40,7 @@ const SvgInline = (props) => {
let body;
let status;
if (isAuthenticated && (!userToken || userToken === 'placeholderPAT')) {
if (isAuthenticated && (!userToken || userToken === "placeholderPAT")) {
// waiting for backend call to private-access
return;
}
@@ -51,7 +51,7 @@ const SvgInline = (props) => {
status = res.status;
} else {
const req = createMockReq({
method: 'GET',
method: "GET",
url: url,
});
const res = createMockRes();
@@ -61,7 +61,7 @@ const SvgInline = (props) => {
}
if (status >= 300) {
console.error('failed to fetch/generate SVG');
console.error("failed to fetch/generate SVG");
return;
}
@@ -83,12 +83,12 @@ const SvgInline = (props) => {
// Attach shadow root if not already present
let shadow = containerRef.current.shadowRoot;
if (!shadow) {
shadow = containerRef.current.attachShadow({ mode: 'open' });
shadow = containerRef.current.attachShadow({ mode: "open" });
}
// Clear previous content
shadow.innerHTML = '';
shadow.innerHTML = "";
// Insert SVG
const wrapper = document.createElement('div');
const wrapper = document.createElement("div");
wrapper.innerHTML = svg;
shadow.appendChild(wrapper);
}
@@ -96,7 +96,7 @@ const SvgInline = (props) => {
if (props.forceLoading || !loaded) {
if (props.compact) {
return <Skeleton style={{ paddingBottom: '58%' }} />;
return <Skeleton style={{ paddingBottom: "58%" }} />;
}
// maximum dimensions of cards in SelectCard stage
return <Skeleton className="h-[245px] w-[450px]" />;
@@ -115,7 +115,7 @@ SvgInline.propTypes = {
};
SvgInline.defaultProps = {
className: '',
className: "",
forceLoading: false,
compact: false,
};
+2 -2
View File
@@ -1,4 +1,4 @@
import SvgInline from './SVG';
import { Card, Image } from './Card';
import SvgInline from "./SVG";
import { Card, Image } from "./Card";
export { Card, Image, SvgInline };
@@ -1,8 +1,8 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import { classnames } from '../../utils';
import { classnames } from "../../utils";
const Button = (props) => {
return (
@@ -11,7 +11,7 @@ const Button = (props) => {
{...props}
className={classnames(
props.className,
'border-0 py-2 px-6 inline-flex focus:outline-none rounded-[0.25rem] text-lg',
"border-0 py-2 px-6 inline-flex focus:outline-none rounded-[0.25rem] text-lg",
)}
>
{props.children}
@@ -25,7 +25,7 @@ Button.propTypes = {
};
Button.defaultProps = {
className: '',
className: "",
};
export default Button;
@@ -1,5 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
const Checkbox = ({ question, variable, setVariable, disabled }) => {
return (
@@ -11,7 +11,7 @@ const Checkbox = ({ question, variable, setVariable, disabled }) => {
<input
type="checkbox"
disabled={disabled}
checked={variable && !disabled ? 'checked' : ''}
checked={variable && !disabled ? "checked" : ""}
className="checkbox mr-2"
onChange={() => setVariable(!variable)}
/>
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import { classnames } from '../../utils';
import { classnames } from "../../utils";
// options is of form [{value: '', label: '', disabled: true/false}]
const Input = ({
@@ -14,15 +14,15 @@ const Input = ({
return (
<select
className={classnames(
'text-gray-700 bg-white select select-sm w-40 rounded-sm mt-4',
"text-gray-700 bg-white select select-sm w-40 rounded-sm mt-4",
className,
)}
value={selectedOption.label}
onChange={(e) =>
onChange={(e) => {
setSelectedOption(
options.find((item) => item.label === e.target.value),
// eslint-disable-next-line prettier/prettier
)}
);
}}
disabled={disabled}
>
{options.map((option) => (
@@ -30,7 +30,7 @@ const Input = ({
key={option.value}
disabled={option.disabled}
className={classnames(
option.label === selectedOption.label && 'bg-blue-200',
option.label === selectedOption.label && "bg-blue-200",
)}
>
{option.label}
@@ -59,7 +59,7 @@ Input.propTypes = {
Input.defaultProps = {
disabled: false,
className: '',
className: "",
};
export default Input;
@@ -1,5 +1,5 @@
import Button from './Button';
import Checkbox from './Checkbox';
import Input from './Input';
import Button from "./Button";
import Checkbox from "./Checkbox";
import Input from "./Input";
export { Button, Checkbox, Input };
@@ -1,10 +1,10 @@
/* eslint-disable react/no-danger */
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import Section from './Section';
import { Checkbox } from '../Generic';
import Section from "./Section";
import { Checkbox } from "../Generic";
const CheckboxSection = ({
title,
@@ -1,33 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import Section from './Section';
import { Input } from '../Generic';
import Section from "./Section";
import { Input } from "../Generic";
export const DEFAULT_OPTION = {
id: 1,
label: 'Normal',
label: "Normal",
disabled: false,
value: 'normal',
value: "normal",
};
const LanguagesLayoutSection = ({ selectedOption, setSelectedOption }) => {
const options = [
DEFAULT_OPTION,
{ id: 2, label: 'Compact', disabled: false, value: 'compact' },
{ id: 3, label: 'Donut', disabled: false, value: 'donut' },
{ id: 2, label: "Compact", disabled: false, value: "compact" },
{ id: 3, label: "Donut", disabled: false, value: "donut" },
{
id: 4,
label: 'Vertical Donut',
label: "Vertical Donut",
disabled: false,
value: 'donut-vertical',
value: "donut-vertical",
},
{ id: 5, label: 'Pie', disabled: false, value: 'pie' },
{ id: 5, label: "Pie", disabled: false, value: "pie" },
{
id: 6,
label: 'Only Languages',
label: "Only Languages",
disabled: false,
value: 'compact&hide_progress=true',
value: "compact&hide_progress=true",
},
];
@@ -1,9 +1,9 @@
/* eslint-disable react/no-danger */
import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import React, { useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import Section from './Section';
import Section from "./Section";
const NumericSection = ({
title,
@@ -73,7 +73,7 @@ NumericSection.defaultProps = {
max: undefined,
step: 1,
disabled: false,
placeholder: '',
placeholder: "",
};
export default NumericSection;
+17 -17
View File
@@ -1,35 +1,35 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import {
FaArrowLeft as LeftArrowIcon,
FaArrowRight as RightArrowIcon,
} from 'react-icons/fa';
} from "react-icons/fa";
import { classnames } from '../../utils';
import { classnames } from "../../utils";
const ProgressSection = ({ num, item, passed, isActive, onClick }) => {
return (
<button
className={classnames(
'w-1/4 flex flex-col mx-2 p-2 cursor-pointer',
passed ? 'border-blue-500' : 'border-gray-500',
isActive ? 'border-t-[14px] -mt-[5px]' : 'border-t-4',
"w-1/4 flex flex-col mx-2 p-2 cursor-pointer",
passed ? "border-blue-500" : "border-gray-500",
isActive ? "border-t-[14px] -mt-[5px]" : "border-t-4",
)}
type="button"
onClick={onClick}
>
<div
className={classnames(
'text-lg font-bold',
passed ? 'text-blue-500' : 'text-gray-500',
isActive ? '-mt-[4px]' : '',
"text-lg font-bold",
passed ? "text-blue-500" : "text-gray-500",
isActive ? "-mt-[4px]" : "",
)}
>
{`Step ${num + 1}`}
</div>
<div className={classnames(passed ? 'text-gray-700' : 'text-gray-500')}>
<div className={classnames(passed ? "text-gray-700" : "text-gray-500")}>
{item}
</div>
</button>
@@ -52,10 +52,10 @@ const ProgressBar = ({ items, currItem, setCurrItem }) => {
<div className="w-full flex items-center sticky top-0 bg-gray-200 z-50 pt-3 pb-1 px-1 md:px-20 shadow-md">
<LeftArrowIcon
className={classnames(
'w-8 h-8',
"w-8 h-8",
leftDisabled
? 'text-gray-400 cursor-not-allowed'
: 'text-gray-700 cursor-pointer',
? "text-gray-400 cursor-not-allowed"
: "text-gray-700 cursor-pointer",
)}
onClick={() => setCurrItem(Math.max(currItem - 1, 0))}
/>
@@ -75,10 +75,10 @@ const ProgressBar = ({ items, currItem, setCurrItem }) => {
</div>
<RightArrowIcon
className={classnames(
'w-8 h-8',
"w-8 h-8",
rightDisabled
? 'text-gray-400 cursor-not-allowed'
: 'text-gray-700 cursor-pointer',
? "text-gray-400 cursor-not-allowed"
: "text-gray-700 cursor-pointer",
)}
onClick={() => setCurrItem(Math.min(currItem + 1, items.length - 1))}
/>
+4 -4
View File
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import { HiOutlineLightningBolt as LightningIcon } from 'react-icons/hi';
import { HiOutlineLightningBolt as LightningIcon } from "react-icons/hi";
const Section = (props) => {
return (
@@ -29,7 +29,7 @@ Section.propTypes = {
};
Section.defaultProps = {
title: 'Test',
title: "Test",
children: <p className="leading-relaxed">This is a test!</p>,
};
@@ -1,22 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import Section from './Section';
import { Input } from '../Generic';
import Section from "./Section";
import { Input } from "../Generic";
export const DEFAULT_OPTION = {
id: 1,
label: 'Rank',
label: "Rank",
disabled: false,
value: 'default',
value: "default",
};
const StatsRankSection = ({ selectedOption, setSelectedOption }) => {
const options = [
DEFAULT_OPTION,
{ id: 2, label: 'Percentile', disabled: false, value: 'percentile' },
{ id: 3, label: 'GitHub', disabled: false, value: 'github' },
{ id: 4, label: 'None', disabled: false, value: 'default&hide_rank=true' },
{ id: 2, label: "Percentile", disabled: false, value: "percentile" },
{ id: 3, label: "GitHub", disabled: false, value: "github" },
{ id: 4, label: "None", disabled: false, value: "default&hide_rank=true" },
];
return (
@@ -1,10 +1,10 @@
/* eslint-disable react/no-danger */
import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import React, { useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import Section from './Section';
import { classnames } from '../../utils';
import Section from "./Section";
import { classnames } from "../../utils";
const TextSection = ({
title,
@@ -43,8 +43,8 @@ const TextSection = ({
<input
type="text"
className={classnames(
'border border-gray-300 rounded px-2 py-1 mt-2 w-3/4 min-w-48 max-w-xl',
disabled ? 'cursor-not-allowed' : '',
"border border-gray-300 rounded px-2 py-1 mt-2 w-3/4 min-w-48 max-w-xl",
disabled ? "cursor-not-allowed" : "",
)}
value={internalValue}
onChange={(e) => setInternalValue(e.target.value)}
@@ -68,7 +68,7 @@ TextSection.propTypes = {
TextSection.defaultProps = {
disabled: false,
placeholder: '',
placeholder: "",
onPaste: undefined,
};
@@ -1,25 +1,25 @@
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import Section from './Section';
import { Input } from '../Generic';
import Section from "./Section";
import { Input } from "../Generic";
export const DEFAULT_OPTION = {
id: 1,
label: 'Normal',
label: "Normal",
disabled: false,
value: 'default',
value: "default",
};
const WakatimeLayoutSection = ({ selectedOption, setSelectedOption }) => {
const options = [
DEFAULT_OPTION,
{ id: 2, label: 'Compact', disabled: false, value: 'compact' },
{ id: 2, label: "Compact", disabled: false, value: "compact" },
{
id: 3,
label: 'Text Only',
label: "Text Only",
disabled: false,
value: 'default&hide_progress=true&card_width=315',
value: "default&hide_progress=true&card_width=315",
},
];
+2 -2
View File
@@ -1,4 +1,4 @@
import ProgressBar from './Progress';
import CheckboxSection from './CheckboxSection';
import ProgressBar from "./Progress";
import CheckboxSection from "./CheckboxSection";
export { ProgressBar, CheckboxSection };
@@ -1,12 +1,12 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
import {
FaArrowRight as ArrowRightIcon,
FaArrowLeft as ArrowLeftIcon,
} from 'react-icons/fa';
} from "react-icons/fa";
import { classnames } from '../../utils';
import { classnames } from "../../utils";
const Preview = ({ pages, details, showArrows }) => {
const totalPages = pages.length;
@@ -38,7 +38,7 @@ const Preview = ({ pages, details, showArrows }) => {
<img
src={pages[page]}
alt="preview"
className={showArrows ? 'w-3/4 mx-auto' : 'w-full'}
className={showArrows ? "w-3/4 mx-auto" : "w-full"}
/>
{showArrows && (
<ArrowRightIcon
@@ -50,8 +50,8 @@ const Preview = ({ pages, details, showArrows }) => {
<br />
<p
className={classnames(
'w-full text-center',
'text-sm lg:text-lg xl:text-xl 2xl:text-2xl',
"w-full text-center",
"text-sm lg:text-lg xl:text-xl 2xl:text-2xl",
)}
>
{details[page]}
@@ -1,3 +1,3 @@
import Preview from './Preview';
import Preview from "./Preview";
export default Preview;
+4 -4
View File
@@ -1,7 +1,7 @@
import Preview from './Preview';
import Preview from "./Preview";
export * from './Generic';
export * from './Card';
export * from './Home';
export * from "./Generic";
export * from "./Card";
export * from "./Home";
export { Preview };
+8 -8
View File
@@ -3,25 +3,25 @@ export const PROD = false;
export const USE_LOGGER = true;
export const CLIENT_ID = 'Ov23lilAc5biyyRY0K1u';
export const CLIENT_ID = "Ov23lilAc5biyyRY0K1u";
export const HOST = PROD
? 'github-stats-extended.vercel.app'
: 'github-stats-extended-preview.vercel.app';
? "github-stats-extended.vercel.app"
: "github-stats-extended-preview.vercel.app";
export const REDIRECT_URI = `https://${HOST}/frontend`;
export const GITHUB_PRIVATE_AUTH_URL = `https://github.com/login/oauth/authorize?scope=user,repo&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}/private`;
export const GITHUB_PUBLIC_AUTH_URL = `https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}/public`;
export const DEMO_USER = 'anuraghazra';
export const DEMO_REPO = 'anuraghazra/github-readme-stats';
export const DEMO_GIST = 'bbfce31e0217a3689c8d961a356cb10d';
export const DEMO_WAKATIME_USER = 'ffflabs';
export const DEMO_USER = "anuraghazra";
export const DEMO_REPO = "anuraghazra/github-readme-stats";
export const DEMO_GIST = "bbfce31e0217a3689c8d961a356cb10d";
export const DEMO_WAKATIME_USER = "ffflabs";
window.process = {
env: {
FETCH_MULTI_PAGE_STARS: 10,
PAT_1: 'placeholderPAT', // so the backend's retryer.js sees there is 1 PAT and sets `RETRIES` accordingly
PAT_1: "placeholderPAT", // so the backend's retryer.js sees there is 1 PAT and sets `RETRIES` accordingly
},
};
+1 -1
View File
@@ -5,7 +5,7 @@
body {
margin: 0;
font-family: 'Segoe UI', Ubuntu, Sans-Serif;
font-family: "Segoe UI", Ubuntu, Sans-Serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
+8 -8
View File
@@ -1,15 +1,15 @@
import './axios-override';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import "./axios-override";
import React from "react";
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import configureStore from './redux/store';
import { AppTrends } from './pages/App';
import './index.css';
import configureStore from "./redux/store";
import { AppTrends } from "./pages/App";
import "./index.css";
export const store = configureStore();
const root = ReactDOM.createRoot(document.getElementById('root'));
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
+3 -3
View File
@@ -1,5 +1,5 @@
export function createMockReq({
method = 'GET',
method = "GET",
url,
headers = {},
body = null,
@@ -32,7 +32,7 @@ export function createMockRes() {
},
write(chunk) {
if (typeof chunk !== 'string') {
if (typeof chunk !== "string") {
chunk = String(chunk);
}
chunks.push(chunk);
@@ -50,7 +50,7 @@ export function createMockRes() {
return { ...headers };
},
_getBody() {
return chunks.join('');
return chunks.join("");
},
};
@@ -34,9 +34,7 @@
"user_view_type": "public",
"site_admin": false
},
"labels": [
],
"labels": [],
"state": "closed",
"locked": false,
"assignee": {
@@ -34,15 +34,11 @@
"user_view_type": "public",
"site_admin": false
},
"labels": [
],
"labels": [],
"state": "open",
"locked": false,
"assignee": null,
"assignees": [
],
"assignees": [],
"milestone": null,
"comments": 2,
"created_at": "2025-11-18T09:48:46Z",
+1 -3
View File
@@ -24,9 +24,7 @@
"errors": [
{
"type": "NOT_FOUND",
"path": [
"organization"
],
"path": ["organization"],
"locations": [
{
"line": 25,
+2 -6
View File
@@ -34,15 +34,11 @@
"user_view_type": "public",
"site_admin": false
},
"labels": [
],
"labels": [],
"state": "open",
"locked": false,
"assignee": null,
"assignees": [
],
"assignees": [],
"milestone": null,
"comments": 2,
"created_at": "2025-11-18T09:48:46Z",
+16 -16
View File
@@ -1,39 +1,39 @@
import React, { useEffect, useState } from 'react';
import { useDispatch } from 'react-redux';
import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { BrowserRouter as Router } from 'react-router-dom';
import { BrowserRouter as Router } from "react-router-dom";
import {
logout as _logout,
setUserAccess as _setUserAccess,
} from '../../redux/actions/userActions';
} from "../../redux/actions/userActions";
import Header from './Header';
import HomeScreen from '../Home';
import { getUserMetadata } from '../../api';
import Header from "./Header";
import HomeScreen from "../Home";
import { getUserMetadata } from "../../api";
import {
useIsAuthenticated,
useUserKey,
useUserToken,
} from '../../redux/selectors/userSelectors';
import { toast, ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { clearAxiosCache } from '../../axios-override';
} from "../../redux/selectors/userSelectors";
import { toast, ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { clearAxiosCache } from "../../axios-override";
function App() {
const toMessage = (input) => {
if (typeof input === 'string') return input;
if (typeof input === "string") return input;
if (input.reason?.message) return input.reason.message;
if (input.message) return input.message;
try {
return JSON.stringify(input);
} catch {
return 'Unknown error';
return "Unknown error";
}
};
const showError = (event) => {
toast.error(toMessage(event), {
position: 'bottom-right',
position: "bottom-right",
autoClose: 1500,
hideProgressBar: true,
closeOnClick: false,
@@ -43,10 +43,10 @@ function App() {
});
};
window.addEventListener('error', (event) => {
window.addEventListener("error", (event) => {
showError(event);
});
window.addEventListener('unhandledrejection', (event) => {
window.addEventListener("unhandledrejection", (event) => {
showError(event);
});
+15 -15
View File
@@ -1,13 +1,13 @@
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import PropTypes from "prop-types";
import { Link } from 'react-router-dom';
import { Link } from "react-router-dom";
import appIcon from '../../assets/appLogo64.png';
import { classnames } from '../../utils';
import { FaGithub as GithubIcon } from 'react-icons/fa';
import { ProgressBar } from '../../components';
import appIcon from "../../assets/appLogo64.png";
import { classnames } from "../../utils";
import { FaGithub as GithubIcon } from "react-icons/fa";
import { ProgressBar } from "../../components";
const propTypes = {
to: PropTypes.string.isRequired,
@@ -25,7 +25,7 @@ const StandardLink = ({ to, children, onClick, className }) => (
<Link
to={to}
className={classnames(
'px-4 py-1 mr-3 rounded-sm bg-gray-200 hover:bg-gray-300 text-gray-700',
"px-4 py-1 mr-3 rounded-sm bg-gray-200 hover:bg-gray-300 text-gray-700",
className,
)}
onClick={onClick}
@@ -42,7 +42,7 @@ const MobileLink = ({ to, children, onClick, className }) => (
<Link
to={to}
className={classnames(
'block text-sm px-2 my-2 py-2 rounded-sm bg-gray-200 text-gray-700',
"block text-sm px-2 my-2 py-2 rounded-sm bg-gray-200 text-gray-700",
className,
)}
onClick={onClick}
@@ -90,11 +90,11 @@ const Header = ({ stage, setStage }) => {
</div>
<ProgressBar
items={[
'Login',
'Select Card',
'Modify Parameters',
'Select Theme',
'Display Card',
"Login",
"Select Card",
"Modify Parameters",
"Select Theme",
"Display Card",
]}
currItem={stage}
setCurrItem={setStage}
+1 -1
View File
@@ -1,3 +1,3 @@
import AppTrends from './AppTrends';
import AppTrends from "./AppTrends";
export { AppTrends };
+45 -45
View File
@@ -1,36 +1,36 @@
import React, { useEffect, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import BounceLoader from 'react-spinners/BounceLoader';
import React, { useEffect, useRef, useState } from "react";
import { useDispatch } from "react-redux";
import PropTypes from "prop-types";
import BounceLoader from "react-spinners/BounceLoader";
import { v4 as uuidv4 } from 'uuid';
import { v4 as uuidv4 } from "uuid";
import {
CustomizeStage,
DisplayStage,
LoginStage,
SelectCardStage,
ThemeStage,
} from './stages';
} from "./stages";
import { authenticate } from '../../api';
import { login as _login } from '../../redux/actions/userActions';
import { authenticate } from "../../api";
import { login as _login } from "../../redux/actions/userActions";
import {
HOST,
DEMO_USER,
DEMO_WAKATIME_USER,
DEMO_REPO,
DEMO_GIST,
} from '../../constants';
import { CardTypes } from '../../utils';
import { DEFAULT_OPTION as STATS_DEFAULT_RANK } from '../../components/Home/StatsRankSection';
import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from '../../components/Home/LanguagesLayoutSection';
import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from '../../components/Home/WakatimeLayoutSection';
} from "../../constants";
import { CardTypes } from "../../utils";
import { DEFAULT_OPTION as STATS_DEFAULT_RANK } from "../../components/Home/StatsRankSection";
import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from "../../components/Home/LanguagesLayoutSection";
import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from "../../components/Home/WakatimeLayoutSection";
import {
useUserId,
useIsAuthenticated,
usePrivateAccess,
} from '../../redux/selectors/userSelectors';
import axios from 'axios';
} from "../../redux/selectors/userSelectors";
import axios from "axios";
const HomeScreen = ({ stage, setStage }) => {
const [isLoading, setIsLoading] = useState(false);
@@ -49,7 +49,7 @@ const HomeScreen = ({ stage, setStage }) => {
const [gist, setGist] = useState(DEMO_GIST);
const [wakatimeUser, setWakatimeUser] = useState(DEMO_WAKATIME_USER);
const [selectedCard, setSelectedCard] = useState('stats');
const [selectedCard, setSelectedCard] = useState("stats");
useEffect(() => {
setSelectedUserId(userId);
@@ -68,7 +68,7 @@ const HomeScreen = ({ stage, setStage }) => {
const [showTitle, setShowTitle] = useState(true);
const [showOwner, setShowOwner] = useState(false);
const [descriptionLines, setDescriptionLines] = useState();
const [customTitle, setCustomTitle] = useState('');
const [customTitle, setCustomTitle] = useState("");
const [langsCount, setLangsCount] = useState();
const [showAllStats, setShowAllStats] = useState(false);
const [showIcons, setShowIcons] = useState(false);
@@ -91,11 +91,11 @@ const HomeScreen = ({ stage, setStage }) => {
setSelectedLanguagesLayout(LANGUAGES_DEFAULT_LAYOUT);
}
if (theme === 'default' || theme === 'default_repocard') {
if (theme === "default" || theme === "default_repocard") {
if (selectedCard === CardTypes.PIN || selectedCard === CardTypes.GIST) {
setTheme('default_repocard');
setTheme("default_repocard");
} else {
setTheme('default');
setTheme("default");
}
}
};
@@ -104,7 +104,7 @@ const HomeScreen = ({ stage, setStage }) => {
resetCustomization();
}, [selectedCard]);
let fullSuffix = `${selectedCard === CardTypes.STATS ? '' : '/' + selectedCard}?`;
let fullSuffix = `${selectedCard === CardTypes.STATS ? "" : "/" + selectedCard}?`;
switch (selectedCard) {
case CardTypes.STATS:
@@ -148,14 +148,14 @@ const HomeScreen = ({ stage, setStage }) => {
selectedCard === CardTypes.TOP_LANGS ||
selectedCard === CardTypes.WAKATIME)
) {
fullSuffix += '&hide_title=true';
fullSuffix += "&hide_title=true";
}
if (
showOwner &&
(selectedCard === CardTypes.PIN || selectedCard === CardTypes.GIST)
) {
fullSuffix += '&show_owner=true';
fullSuffix += "&show_owner=true";
}
if (descriptionLines && selectedCard === CardTypes.PIN) {
@@ -204,16 +204,16 @@ const HomeScreen = ({ stage, setStage }) => {
}
// for stage four
const [theme, setTheme] = useState('default');
const [theme, setTheme] = useState("default");
let themeSuffix = fullSuffix;
if (
!(
(theme === 'default' &&
(theme === "default" &&
[CardTypes.STATS, CardTypes.TOP_LANGS, CardTypes.WAKATIME].includes(
selectedCard,
)) ||
(theme === 'default_repocard' &&
(theme === "default_repocard" &&
[CardTypes.PIN, CardTypes.GIST].includes(selectedCard))
)
) {
@@ -221,7 +221,7 @@ const HomeScreen = ({ stage, setStage }) => {
}
// for stage five
const [gistUrl, setGistUrl] = useState('');
const [gistUrl, setGistUrl] = useState("");
let guestHint;
switch (selectedCard) {
@@ -247,7 +247,7 @@ const HomeScreen = ({ stage, setStage }) => {
return result.data.html_url;
} catch (error) {
console.error(error);
return '';
return "";
}
};
@@ -274,9 +274,9 @@ const HomeScreen = ({ stage, setStage }) => {
const url = window.location.href;
// If Github API returns the code parameter
if (url.includes('code=')) {
const tempPrivateAccess = url.includes('private');
const newUrl = url.split('?code=');
if (url.includes("code=")) {
const tempPrivateAccess = url.includes("private");
const newUrl = url.split("?code=");
const redirect = `${url.split(HOST)[0]}${HOST}/frontend`;
window.history.pushState({}, null, redirect);
setIsLoading(true);
@@ -313,11 +313,11 @@ const HomeScreen = ({ stage, setStage }) => {
<div className="text-2xl text-gray-600 font-semibold">
{
[
'Login',
'Select a Card',
'Modify Card Parameters',
'Choose a Theme',
'Display your Card',
"Login",
"Select a Card",
"Modify Card Parameters",
"Choose a Theme",
"Display your Card",
][stage]
}
</div>
@@ -325,7 +325,7 @@ const HomeScreen = ({ stage, setStage }) => {
{stage === 0 && isAuthenticated ? (
<div>
<p>
You are logged in as{' '}
You are logged in as{" "}
<a
href={`https://github.com/${userId}`}
target="_blank"
@@ -338,12 +338,12 @@ const HomeScreen = ({ stage, setStage }) => {
<p>
{privateAccess ? (
<>
You granted GitHub-Stats-Extended access to both your{' '}
You granted GitHub-Stats-Extended access to both your{" "}
<b>public and private</b> contributions.
</>
) : (
<>
You granted GitHub-Stats-Extended access to your{' '}
You granted GitHub-Stats-Extended access to your{" "}
<b>public</b> contributions.
</>
)}
@@ -351,11 +351,11 @@ const HomeScreen = ({ stage, setStage }) => {
</div>
) : (
[
'',
'You will be able to customize your card in future steps.',
'',
'',
'Display the finished card on GitHub, Twitter/X, LinkedIn, or anywhere else!',
"",
"You will be able to customize your card in future steps.",
"",
"",
"Display the finished card on GitHub, Twitter/X, LinkedIn, or anywhere else!",
][stage]
)}
</div>
@@ -439,7 +439,7 @@ const HomeScreen = ({ stage, setStage }) => {
return `https://${HOST}/api${themeSuffix}`;
case CardTypes.PIN:
let myRepo = repo;
if (!myRepo.includes('/')) {
if (!myRepo.includes("/")) {
myRepo = `${userId}/${myRepo}`;
}
return `https://github.com/${myRepo}`;
+1 -1
View File
@@ -1,3 +1,3 @@
import HomeScreen from './Home';
import HomeScreen from "./Home";
export default HomeScreen;
@@ -1,20 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import { CheckboxSection, Image } from '../../../components';
import { CardTypes } from '../../../utils';
import TextSection from '../../../components/Home/TextSection';
import NumericSection from '../../../components/Home/NumericSection';
import StatsRankSection from '../../../components/Home/StatsRankSection';
import LanguagesLayoutSection from '../../../components/Home/LanguagesLayoutSection';
import WakatimeLayoutSection from '../../../components/Home/WakatimeLayoutSection';
import { CheckboxSection, Image } from "../../../components";
import { CardTypes } from "../../../utils";
import TextSection from "../../../components/Home/TextSection";
import NumericSection from "../../../components/Home/NumericSection";
import StatsRankSection from "../../../components/Home/StatsRankSection";
import LanguagesLayoutSection from "../../../components/Home/LanguagesLayoutSection";
import WakatimeLayoutSection from "../../../components/Home/WakatimeLayoutSection";
import {
DEMO_GIST,
DEMO_REPO,
DEMO_USER,
DEMO_WAKATIME_USER,
} from '../../../constants';
import { useIsAuthenticated } from '../../../redux/selectors/userSelectors';
} from "../../../constants";
import { useIsAuthenticated } from "../../../redux/selectors/userSelectors";
const CustomizeStage = ({
selectedCard,
@@ -70,7 +70,7 @@ const CustomizeStage = ({
<br />
{!isAuthenticated && (
<>
Please{' '}
Please{" "}
<a
href="#"
onClick={(e) => {
@@ -80,7 +80,7 @@ const CustomizeStage = ({
className="underline text-blue-900"
>
log in
</a>{' '}
</a>{" "}
to change the username.
</>
)}
@@ -91,14 +91,14 @@ const CustomizeStage = ({
setValue={setSelectedUserId}
onPaste={(e) => {
e.preventDefault();
let newValue = e.clipboardData.getData('text');
let newValue = e.clipboardData.getData("text");
// if the user pasted a full GitHub URL, extract username
if (newValue.endsWith('/')) {
if (newValue.endsWith("/")) {
newValue = newValue.slice(0, -1);
}
let parts = newValue.split('/');
let parts = newValue.split("/");
if (parts.length > 1) {
newValue = parts.slice(-1).join('/');
newValue = parts.slice(-1).join("/");
}
setSelectedUserId(newValue);
}}
@@ -114,7 +114,7 @@ const CustomizeStage = ({
<br />
{!isAuthenticated && (
<>
Please{' '}
Please{" "}
<a
href="#"
onClick={(e) => {
@@ -124,7 +124,7 @@ const CustomizeStage = ({
className="underline text-blue-900"
>
log in
</a>{' '}
</a>{" "}
to change the repo.
</>
)}
@@ -135,14 +135,14 @@ const CustomizeStage = ({
setValue={setRepo}
onPaste={(e) => {
e.preventDefault();
let newValue = e.clipboardData.getData('text');
let newValue = e.clipboardData.getData("text");
// if the user pasted a full GitHub URL, extract owner/repo
if (newValue.endsWith('/')) {
if (newValue.endsWith("/")) {
newValue = newValue.slice(0, -1);
}
let parts = newValue.split('/');
let parts = newValue.split("/");
if (parts.length > 2) {
newValue = parts.slice(-2).join('/');
newValue = parts.slice(-2).join("/");
}
setRepo(newValue);
}}
@@ -158,7 +158,7 @@ const CustomizeStage = ({
<br />
{!isAuthenticated && (
<>
Please{' '}
Please{" "}
<a
href="#"
onClick={(e) => {
@@ -168,7 +168,7 @@ const CustomizeStage = ({
className="underline text-blue-900"
>
log in
</a>{' '}
</a>{" "}
to change the gist id.
</>
)}
@@ -179,14 +179,14 @@ const CustomizeStage = ({
setValue={setGist}
onPaste={(e) => {
e.preventDefault();
let newValue = e.clipboardData.getData('text');
let newValue = e.clipboardData.getData("text");
// if the user pasted a full GitHub URL, extract Gist ID
if (newValue.endsWith('/')) {
if (newValue.endsWith("/")) {
newValue = newValue.slice(0, -1);
}
let parts = newValue.split('/');
let parts = newValue.split("/");
if (parts.length > 1) {
newValue = parts.slice(-1).join('/');
newValue = parts.slice(-1).join("/");
}
setGist(newValue);
}}
@@ -198,14 +198,14 @@ const CustomizeStage = ({
title="WakaTime Username"
description={
<>
Set your{' '}
Set your{" "}
<a
href="https://wakatime.com/"
target="_blank"
className="underline text-blue-900"
>
WakaTime
</a>{' '}
</a>{" "}
username to fetch your stats.
</>
}
@@ -336,14 +336,14 @@ const CustomizeStage = ({
/>
)}
<div className="pl-10 pr-10">
For more customization options check the{' '}
For more customization options check the{" "}
<a
href="https://github.com/stats-organization/github-stats-extended/blob/master/docs/advanced_documentation.md"
target="_blank"
className="underline text-blue-900"
>
customization documentation
</a>{' '}
</a>{" "}
after you copied your card URL in step 5.
</div>
</div>
+20 -20
View File
@@ -1,23 +1,23 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { saveSvgAsPng } from 'save-svg-as-png';
import { saveSvgAsPng } from "save-svg-as-png";
import { Button, Image } from '../../../components';
import { classnames } from '../../../utils';
import { HOST } from '../../../constants';
import { Button, Image } from "../../../components";
import { classnames } from "../../../utils";
import { HOST } from "../../../constants";
const DisplayStage = ({ filename, link, themeSuffix, guestHint }) => {
const card = themeSuffix.split('?')[0];
const card = themeSuffix.split("?")[0];
const downloadPNG = () => {
saveSvgAsPng(
document.getElementById('svgWrapper').shadowRoot.firstElementChild
document.getElementById("svgWrapper").shadowRoot.firstElementChild
.firstElementChild,
`${filename}.png`,
{
@@ -31,8 +31,8 @@ const DisplayStage = ({ filename, link, themeSuffix, guestHint }) => {
navigator.clipboard.writeText(
`[![GitHub Stats](https://${HOST}/api${themeSuffix})](${link})`,
);
toast.info('Copied to Clipboard!', {
position: 'bottom-right',
toast.info("Copied to Clipboard!", {
position: "bottom-right",
autoClose: 1500,
hideProgressBar: true,
closeOnClick: false,
@@ -44,8 +44,8 @@ const DisplayStage = ({ filename, link, themeSuffix, guestHint }) => {
const copyUrl = () => {
navigator.clipboard.writeText(`https://${HOST}/api${themeSuffix}`);
toast.info('Copied to Clipboard!', {
position: 'bottom-right',
toast.info("Copied to Clipboard!", {
position: "bottom-right",
autoClose: 1500,
hideProgressBar: true,
closeOnClick: false,
@@ -62,20 +62,20 @@ const DisplayStage = ({ filename, link, themeSuffix, guestHint }) => {
<div className="flex flex-col items-center">
{[
{
title: 'Copy Markdown',
title: "Copy Markdown",
highlight: true,
onClick: copyMarkdown,
},
{ title: 'Copy URL', highlight: false, onClick: copyUrl },
{ title: 'Download PNG', highlight: false, onClick: downloadPNG },
{ title: "Copy URL", highlight: false, onClick: copyUrl },
{ title: "Download PNG", highlight: false, onClick: downloadPNG },
].map((item, index) => (
<Button
key={index}
className={classnames(
'm-4 w-60 flex justify-center',
"m-4 w-60 flex justify-center",
item.highlight
? 'bg-blue-500 hover:bg-blue-600 text-white'
: 'bg-white hover:bg-gray-100 text-black',
? "bg-blue-500 hover:bg-blue-600 text-white"
: "bg-white hover:bg-gray-100 text-black",
)}
onClick={item.onClick}
>
+18 -18
View File
@@ -1,11 +1,11 @@
/* eslint-disable react/no-array-index-key */
import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch } from 'react-redux';
import React, { useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import { useDispatch } from "react-redux";
import { Button, Image } from '../../../components';
import { classnames } from '../../../utils';
import { Button, Image } from "../../../components";
import { classnames } from "../../../utils";
import {
CLIENT_ID,
DEMO_GIST,
@@ -15,16 +15,16 @@ import {
GITHUB_PRIVATE_AUTH_URL,
GITHUB_PUBLIC_AUTH_URL,
HOST,
} from '../../../constants';
import { FaGithub as GithubIcon } from 'react-icons/fa';
import { logout as _logout } from '../../../redux/actions/userActions';
} from "../../../constants";
import { FaGithub as GithubIcon } from "react-icons/fa";
import { logout as _logout } from "../../../redux/actions/userActions";
import {
useIsAuthenticated,
usePrivateAccess,
useUserId,
useUserKey,
} from '../../../redux/selectors/userSelectors';
import { deleteAccount } from '../../../api';
} from "../../../redux/selectors/userSelectors";
import { deleteAccount } from "../../../api";
const LoginStage = ({ setCurrItem }) => {
const userId = useUserId(null);
@@ -59,10 +59,10 @@ const LoginStage = ({ setCurrItem }) => {
}
// Bind the event listener
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener("mousedown", handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener("mousedown", handleClickOutside);
};
}, [ref]);
}
@@ -99,12 +99,12 @@ const LoginStage = ({ setCurrItem }) => {
return (
<div className="h-full flex flex-wrap">
<div className={classnames(deleteModal ? 'opacity-25' : '', 'md:flex')}>
<div className={classnames(deleteModal ? "opacity-25" : "", "md:flex")}>
<div className="lg:block lg:w-3/5 lg:p-8">
<div
className={classnames(
'bg-gray-200 rounded-sm w-full h-full m-auto p-8 shadow',
'lg:h-auto',
"bg-gray-200 rounded-sm w-full h-full m-auto p-8 shadow",
"lg:h-auto",
)}
>
{isAuthenticated ? (
@@ -243,9 +243,9 @@ const LoginStage = ({ setCurrItem }) => {
key={index}
style={{
left: `${x}%`,
position: 'relative',
zoom: '0.5',
marginBottom: '1%',
position: "relative",
zoom: "0.5",
marginBottom: "1%",
}}
>
<Image
@@ -1,51 +1,51 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import { Card } from '../../../components';
import { useUserId } from '../../../redux/selectors/userSelectors';
import { Card } from "../../../components";
import { useUserId } from "../../../redux/selectors/userSelectors";
import {
DEMO_GIST,
DEMO_REPO,
DEMO_USER,
DEMO_WAKATIME_USER,
} from '../../../constants';
import { CardTypes } from '../../../utils';
} from "../../../constants";
import { CardTypes } from "../../../utils";
const SelectCardStage = ({ selectedCard, setSelectedCard, setStage }) => {
return (
<div className="w-full flex flex-wrap">
{[
{
title: 'GitHub Stats Card',
description: 'your overall GitHub statistics',
title: "GitHub Stats Card",
description: "your overall GitHub statistics",
demoImageSrc: `?username=${useUserId(DEMO_USER)}&include_all_commits=true`,
cardType: CardTypes.STATS,
},
{
title: 'Top Languages Card',
description: 'your most frequently used languages',
title: "Top Languages Card",
description: "your most frequently used languages",
demoImageSrc: `/top-langs?username=${useUserId(DEMO_USER)}&langs_count=4`,
cardType: CardTypes.TOP_LANGS,
},
{
title: 'GitHub Extra Pin',
title: "GitHub Extra Pin",
description:
'pin more than 6 repositories in your profile using a GitHub profile readme',
"pin more than 6 repositories in your profile using a GitHub profile readme",
demoImageSrc: `/pin?repo=${DEMO_REPO}`,
cardType: CardTypes.PIN,
},
{
title: 'GitHub Gist Pin',
title: "GitHub Gist Pin",
description:
'pin gists in your GitHub profile using a GitHub profile readme',
"pin gists in your GitHub profile using a GitHub profile readme",
demoImageSrc: `/gist?id=${DEMO_GIST}`,
cardType: CardTypes.GIST,
},
{
title: 'WakaTime Stats Card',
description: 'your coding activity from WakaTime',
title: "WakaTime Stats Card",
description: "your coding activity from WakaTime",
demoImageSrc: `/wakatime?username=${DEMO_WAKATIME_USER}&langs_count=6&card_width=450`,
cardType: CardTypes.WAKATIME,
},
+12 -12
View File
@@ -1,10 +1,10 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import PropTypes from "prop-types";
import { Card } from '../../../components';
import { themes } from '../../../backend/themes/index';
import { Card } from "../../../components";
import { themes } from "../../../backend/themes/index";
const ThemeStage = ({ theme, setTheme, setStage, fullSuffix }) => {
return (
@@ -14,12 +14,12 @@ const ThemeStage = ({ theme, setTheme, setStage, fullSuffix }) => {
.filter(
(myTheme) =>
![
'merko',
'blue-green',
'gotham',
'blueberry',
'outrun',
'holi',
"merko",
"blue-green",
"gotham",
"blueberry",
"outrun",
"holi",
].includes(myTheme),
)
.map((myTheme, index) => (
@@ -43,14 +43,14 @@ const ThemeStage = ({ theme, setTheme, setStage, fullSuffix }) => {
))}
</div>
<div className="pl-10 pr-10">
For more theme options check the{' '}
For more theme options check the{" "}
<a
href="https://github.com/stats-organization/github-stats-extended/blob/master/docs/advanced_documentation.md#themes"
target="_blank"
className="underline text-blue-900"
>
customization documentation
</a>{' '}
</a>{" "}
after you copied your card URL in step 5.
</div>
</>
+5 -5
View File
@@ -1,8 +1,8 @@
import LoginStage from './Login';
import SelectCardStage from './SelectCard';
import CustomizeStage from './Customize';
import ThemeStage from './Theme';
import DisplayStage from './Display';
import LoginStage from "./Login";
import SelectCardStage from "./SelectCard";
import CustomizeStage from "./Customize";
import ThemeStage from "./Theme";
import DisplayStage from "./Display";
export {
LoginStage,
@@ -1,6 +1,6 @@
export const LOGIN = 'LOGIN';
export const LOGOUT = 'LOGOUT';
export const SET_USER_ACCESS = 'SET_USER_ACCESS';
export const LOGIN = "LOGIN";
export const LOGOUT = "LOGOUT";
export const SET_USER_ACCESS = "SET_USER_ACCESS";
export function login(userId, userKey) {
return { type: LOGIN, payload: { userId, userKey } };
+2 -2
View File
@@ -1,5 +1,5 @@
import { combineReducers } from 'redux';
import { combineReducers } from "redux";
import user from './user';
import user from "./user";
export default combineReducers({ user });
+6 -6
View File
@@ -1,8 +1,8 @@
import * as types from '../actions/userActions';
import * as types from "../actions/userActions";
const initialState = {
userId: JSON.parse(localStorage.getItem('userId')) || null,
userKey: JSON.parse(localStorage.getItem('userKey')) || null,
userId: JSON.parse(localStorage.getItem("userId")) || null,
userKey: JSON.parse(localStorage.getItem("userKey")) || null,
token: null,
privateAccess: null,
};
@@ -11,8 +11,8 @@ const initialState = {
export default (state = initialState, action) => {
switch (action.type) {
case types.LOGIN:
localStorage.setItem('userId', JSON.stringify(action.payload.userId));
localStorage.setItem('userKey', JSON.stringify(action.payload.userKey));
localStorage.setItem("userId", JSON.stringify(action.payload.userId));
localStorage.setItem("userKey", JSON.stringify(action.payload.userKey));
return {
...state,
userId: action.payload.userId,
@@ -21,7 +21,7 @@ export default (state = initialState, action) => {
case types.LOGOUT:
if (
action.payload.userKey !== null &&
action.payload.userKey !== JSON.parse(localStorage.getItem('userKey'))
action.payload.userKey !== JSON.parse(localStorage.getItem("userKey"))
) {
return state;
}
@@ -1,4 +1,4 @@
import { useSelector } from 'react-redux';
import { useSelector } from "react-redux";
export const useUserId = (fallbackUsername) => {
return useSelector((state) => state.user.userId) || fallbackUsername;
+4 -4
View File
@@ -1,8 +1,8 @@
import { applyMiddleware, createStore, compose } from 'redux';
import { applyMiddleware, createStore, compose } from "redux";
import loggerMiddleware from './logger';
import rootReducer from './reducers';
import { USE_LOGGER } from '../constants';
import loggerMiddleware from "./logger";
import rootReducer from "./reducers";
import { USE_LOGGER } from "../constants";
export default function configureStore(intialState) {
let middlewares = [];
+6 -6
View File
@@ -1,11 +1,11 @@
export function classnames(...args) {
return args.join(' ');
return args.join(" ");
}
export const CardTypes = {
STATS: 'stats',
TOP_LANGS: 'top-langs',
PIN: 'pin',
GIST: 'gist',
WAKATIME: 'wakatime',
STATS: "stats",
TOP_LANGS: "top-langs",
PIN: "pin",
GIST: "gist",
WAKATIME: "wakatime",
};
+3 -3
View File
@@ -1,9 +1,9 @@
import axios from 'axios';
import { HOST } from './constants';
import axios from "axios";
import { HOST } from "./constants";
const fetchWakatimeStats = async ({ username, api_domain }) => {
if (!username) {
throw new Error('missing parameter: username');
throw new Error("missing parameter: username");
}
const { data } = await axios.get(
+9 -9
View File
@@ -1,21 +1,21 @@
/* eslint-disable global-require */
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: {
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
'3xl': '1792px',
sm: "640px",
md: "768px",
lg: "1024px",
xl: "1280px",
"2xl": "1536px",
"3xl": "1792px",
},
extend: {
fontFamily: {
typist: ['Playfair Display SC', 'Courier New'],
typist: ["Playfair Display SC", "Courier New"],
},
},
},
plugins: [require('daisyui')],
plugins: [require("daisyui")],
};
+2 -1
View File
@@ -1,5 +1,6 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"installCommand": "../../vercel-preparation.sh && pnpm install",
"buildCommand": "pnpm run build-trends",
"outputDirectory": "build"
}
}