Merge pull request #31 from LucidKobold/static-page

Static page
This commit is contained in:
Lucid Kobold
2025-08-08 19:46:30 -04:00
committed by GitHub
76 changed files with 9473 additions and 7089 deletions

View File

@@ -1,25 +0,0 @@
{
"rules": {
"comma-dangle": [
"error",
{
"arrays": "never",
"objects": "never",
"imports": "never",
"exports": "never",
"functions": "never"
}
]
},
"extends": [
"next",
"next/core-web-vitals",
"plugin:jsx-a11y/strict",
"prettier",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended"
],
"plugins": ["@typescript-eslint", "react-hooks", "jsx-a11y"]
}

View File

@@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
node-version: [14.x, 16.x, 18.x, 20.x]
node-version: [18.x, 20.x, 22.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
@@ -34,6 +34,10 @@ jobs:
# # - run: npm run start
# - run: npm run test
# Corepack
- name: Enable Corepack
run: corepack enable
# YARN
- uses: borales/actions-yarn@v3.0.0
with:

21
.gitignore vendored
View File

@@ -3,7 +3,12 @@
# dependencies
/node_modules
/.pnp
.pnp.js
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
@@ -23,12 +28,14 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
# env files (can opt-in for committing if needed)
.env*
# yarn
.yarn/*
!.yarn/releases
!.yarn/plugins
.pnp.*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +0,0 @@
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.6.4.cjs

View File

@@ -1,7 +1 @@
compressionLevel: mixed
enableGlobalCache: false
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.1.0.cjs

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Lucid Kobold
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,64 +0,0 @@
import {
format,
getDaysInMonth,
isBefore,
setDate,
startOfDay
} from "date-fns";
/**
* Generated a valid sticker value for use when generating a sticker obj.
* @returns {ValidStickerVal} a number that will represent a valid sticker value.
*/
const generateSticker = (): -2 | -1 | 0 | 1 | 2 => {
const sticker = Math.floor(Math.random() * (2 - -2 + 1)) + -2;
if (
sticker === -2 ||
sticker === -1 ||
sticker === 0 ||
sticker === 1 ||
sticker === 2
) {
return sticker;
}
};
// TODO: Update so seeder takes in a month or date to then generate the stickers for it.
/**
* This seeder is to simulate the date and sticker info from the database.
* Filling up an array for the current month with sticker from ths first to
* the day before the current date, leaving the rest of the month empty.
* @returns {StickerDays} an array with populated sticker objects that correspond to the current month's info.
*/
const stickersSeeder = (): StickerDays => {
const stickers = [] as Sticker[];
const now = startOfDay(new Date());
const daysOfThisMonth = getDaysInMonth(now);
for (let i = 1; i <= daysOfThisMonth; i++) {
const currDate = setDate(now, i);
const sticker = isBefore(currDate, now) ? generateSticker() : null;
const id =
format(currDate, "yyyyddLL") + `/${sticker === null ? 0 : sticker}`;
const newSticker: Sticker = {
id: id,
date: currDate.toJSON(),
sticker: sticker,
edited: false,
manual: false
};
stickers.push(newSticker);
}
if (stickers.length === daysOfThisMonth) {
return stickers;
}
};
export default stickersSeeder;

34
eslint.config.mjs Normal file
View File

@@ -0,0 +1,34 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
import jsxA11y from "eslint-plugin-jsx-a11y";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
import reactPlugin from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
eslintPluginPrettierRecommended,
reactPlugin.configs.flat.recommended,
reactPlugin.configs.flat["jsx-runtime"],
eslint.configs.recommended,
{
plugins: {
jsxA11y: jsxA11y.configs.strict,
"react-hooks": reactHooks,
tseslint: tseslint.configs.recommended,
"tseslint-styles": tseslint.configs.stylistic
}
}
];
export default eslintConfig;

View File

@@ -1,17 +0,0 @@
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
# This is for local/dev. Make sure to update ./prisma/schema.prisma to use the dev config.
DATABASE_URL="postgresql://postgres:randompassword@localhost:5432/dbname?schema=public"
# These are values from Vercel. Make sure to update ./prisma/schema.prisma to use the vercel config.
POSTGRES_DATABASE="dbname"
POSTGRES_PASSWORD="randompassword"
POSTGRES_HOST="region.postgres.vercel-storage.com"
POSTGRES_USER="postgres"
POSTGRES_PRISMA_URL="postgres://postgres:randompassword@region.postgres.vercel-storage.com/dbname?pgbouncer=true&connect_timeout=15"
POSTGRES_URL_NON_POOLING="postgres://postgres:randompassword@region.postgres.vercel-storage.com/dbname"
POSTGRES_URL="postgres://postgres:randompassword@region.postgres.vercel-storage.com/dbname"

View File

@@ -1,14 +0,0 @@
# This section is only applicable during the beta and will be removed when the app is completed.
NEXT_PUBLIC_APP_VERSION="0.2.0"
# Auth Secrets
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=""
# Used when sending emails to users
EMAIL_SERVER_USER=""
EMAIL_SERVER_PASSWORD=""
SMTP_SERVER_HOST=""
SMTP_SERVER_PORT=""
EMAIL_FROM=""
GOOGLE_ID=""
GOOGLE_SECRET=""

View File

@@ -1,179 +0,0 @@
import {
getDate,
endOfMonth,
format,
startOfMonth,
set,
isAfter,
isBefore,
subMonths,
addDays
} from "date-fns";
const weekDays: WeekDays = {
sunday: [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
],
monday: [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
]
};
/**
* Using date-fns, this function checks if currDate is within the month of selectedDate or not.
* @param {Date} selectedDate The current month.
* @param {Date} currDate The date to be compared to the selected month.
* @returns True if currDate is outside of the month of selectedDate, false if otherwise.
*/
const isOverflow = (
selectedDate: Date,
currDate: Date
): {
isOverflow: boolean;
overflowDirection: "prev" | "next" | null;
} => {
let flag = false;
let direction: "next" | "prev" | null = null;
const start = startOfMonth(selectedDate);
const end = endOfMonth(selectedDate);
if (isBefore(currDate, start)) {
flag = true;
direction = "prev";
}
if (isAfter(currDate, end)) {
flag = true;
direction = "next";
}
return { isOverflow: flag, overflowDirection: direction };
};
/**
* A function that will return a month layout when given a date. It produces
* an object with 6 weeks that include overflow from the previous and next month
* with all dates aligned with the day of the week.
* @param selectedDate The date of the month to generate a month layout for.
* @returns The month layout object for the provided month.
*/
const populateMonth = (selectedDate: Date): MonthLayout => {
const endLastMonth = getDate(endOfMonth(subMonths(selectedDate, 1)));
const startOfSelectedMonth = format(startOfMonth(selectedDate), "iii");
const ISOToIndex = {
sunday: {
Sun: 0,
Mon: 1,
Tue: 2,
Wed: 3,
Thu: 4,
Fri: 5,
Sat: 6
},
monday: {
Mon: -1,
Tue: 0,
Wed: 1,
Thu: 2,
Fri: 3,
Sat: 4,
Sun: 5
}
};
const sundays = {
week1: new Array(7).fill(null),
week2: new Array(7).fill(null),
week3: new Array(7).fill(null),
week4: new Array(7).fill(null),
week5: new Array(7).fill(null),
week6: new Array(7).fill(null)
};
// The date of the first day in the overflow
const sunStartDay =
endLastMonth - (ISOToIndex.sunday[startOfSelectedMonth] - 1);
let sunCurrDate = set(subMonths(selectedDate, 1), {
date: sunStartDay
});
for (const week in sundays) {
const thisWeek = sundays[week];
thisWeek.forEach((e, i) => {
const overflowInfo = isOverflow(selectedDate, sunCurrDate);
const day: MonthDay = {
...overflowInfo,
date: sunCurrDate.toJSON()
};
sunCurrDate = addDays(sunCurrDate, 1);
sundays[week][i] = day;
});
}
const mondays = {
week1: new Array(7).fill(null),
week2: new Array(7).fill(null),
week3: new Array(7).fill(null),
week4: new Array(7).fill(null),
week5: new Array(7).fill(null),
week6: new Array(7).fill(null)
};
// The date of the first day in the overflow
const monStartDay = endLastMonth - ISOToIndex.monday[startOfSelectedMonth];
let monCurrDate = set(subMonths(selectedDate, 1), {
date: monStartDay
});
for (const week in mondays) {
const thisWeek = mondays[week];
thisWeek.forEach((e, i) => {
const overflowInfo = isOverflow(selectedDate, monCurrDate);
const day: MonthDay = {
...overflowInfo,
date: monCurrDate.toJSON()
};
monCurrDate = addDays(monCurrDate, 1);
mondays[week][i] = day;
});
}
const output = {
sunday: {
weekdays: weekDays.sunday,
month: sundays
},
monday: {
weekdays: weekDays.monday,
month: mondays
}
};
return output;
};
export default populateMonth;

View File

@@ -1,22 +0,0 @@
/**
* Function to convert the version string to a number tha represents the most recent major release.
* @param {string }version The version string.
* @returns {number} a number that represents the most recent major release.
*/
const versionStringToNumber = (version: string): number => {
const versionStrArr: string[] = version.split(".");
const versionArr: number[] = versionStrArr.map((str) => parseInt(str));
if (versionArr[0] === 0 && versionArr[1] === 0 && versionArr[2] > 1) {
versionArr[1] = 1;
}
const versionStr = `${versionArr[0]}` + "." + `${versionArr[1]}`;
const versionNum: number = parseFloat(versionStr);
return versionNum;
};
export default versionStringToNumber;

2
next-env.d.ts vendored
View File

@@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

9
next.config.ts Normal file
View File

@@ -0,0 +1,9 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
optimizePackageImports: ["@chakra-ui/react"]
}
};
export default nextConfig;

5405
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,15 @@
{
"private": true,
"name": "lucid-creations-website",
"homepage": "https://new.lucidcreations.media/",
"version": "0.1.1",
"version": "0.1.0",
"private": true,
"description": "This is a personal website that I have no specific goals for other than to have fun and share things that I want to.",
"author": {
"name": "Lucid Creations Media",
"url": "https://lucidcreations.media",
"email": "social@lucidcreations.media"
},
"license": "MIT",
"scripts": {
"dev": "next dev",
"build": "next build",
@@ -16,39 +18,32 @@
"pretty": "prettier --write ."
},
"dependencies": {
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@fontsource/anonymous-pro": "^5.0.12",
"@fontsource/anybody": "^5.0.19",
"@fontsource/kalam": "^5.0.12",
"@fontsource/montserrat": "^5.0.17",
"@fontsource/tilt-neon": "^5.0.4",
"@iconify/react": "^4.1.1",
"@reduxjs/toolkit": "^2.2.2",
"date-fns": "^3.6.0",
"formik": "^2.4.5",
"framer-motion": "^11.0.20",
"next": "14.1.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^9.1.0",
"sharp": "^0.33.3"
"@chakra-ui/react": "^3.24.2",
"@emotion/react": "^11.14.0",
"next": "15.4.6",
"next-themes": "^0.4.6",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-icons": "^5.5.0"
},
"devDependencies": {
"@types/node": "^20.11.30",
"@types/react": "^18.2.70",
"@types/react-redux": "^7.1.33",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",
"eslint": "^8.57.0",
"eslint-config-next": "<13.4.9",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"prettier": "^3.2.5",
"typescript": "^5.4.3"
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.33.0",
"@types/node": "^24.2.1",
"@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7",
"@typescript-eslint/eslint-plugin": "^8.39.0",
"@typescript-eslint/parser": "^8.39.0",
"eslint": "^9.33.0",
"eslint-config-next": "15.4.6",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"prettier": "3.6.2",
"typescript": "^5.9.2",
"typescript-eslint": "^8.39.0"
},
"packageManager": "yarn@4.1.0"
"packageManager": "yarn@4.9.2"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,212 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="708px" height="708px" viewBox="0 0 708 708" enable-background="new 0 0 708 708" xml:space="preserve"> <image id="image0" width="708" height="708" x="0" y="0"
xlink:href="data:image/png;base64,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" />
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 565.83 767.39"><defs><style>.b{fill:#fff;}</style></defs><path class="b" d="M454.31,163.14h89.44L422.36,28.42v102.76c0,17.62,14.33,31.96,31.96,31.96Z"/><path class="b" d="M454.31,198.55c-37.15,0-67.38-30.22-67.38-67.37V.45C228.59,.15,103.31,0,33.48,0,15.02,0,0,15.02,0,33.47V733.33c0,18.45,15.02,33.47,33.48,33.47h43.41l.65,.58h455.76c.79-.17,1.72-.33,2.77-.42,15.49-1.33,28.18-14.13,29.57-29.78,.13-1.41,.2-2.66,.2-3.85V198.57l-111.52-.02Zm24.29,207.29c-1.99,1.95-4.25,3.65-6.55,5.24-11.81,8.13-22.84,17.18-33.09,27.21-6.32,6.18-12.28,12.69-17.99,19.43-6.27,7.41-10.84,15.71-14.08,24.9-7,19.92-17.49,37.98-30.25,54.73-14.72,19.32-27.55,39.83-38.81,61.33-3.17,6.06-6.35,12.09-10.69,17.44-9.67,11.92-22.08,19.36-37.12,22.21-12.18,2.31-23.48-.42-33.89-6.97-11.74-7.39-20.41-17.4-25.99-30.16-8.39-19.2-19.42-36.81-32.48-53.16-5.23-6.55-10.63-12.97-15.34-19.92-10.44-15.4-18.45-32-24.58-49.55-.97-2.78-1.94-5.57-3.07-8.29-.53-1.27-1.28-2.54-2.21-3.56-9.96-10.91-19.96-21.79-29.98-32.65-6.4-6.94-13.86-12.63-21.64-17.92-5.24-3.56-10.14-7.53-14.64-11.99-.13-.13-.23-.28-.4-.47,1.91-2.94,4.49-5.09,7.78-6.1,3.52-1.08,7.15-1.79,10.73-2.65,1.17-.28,2.35-.57,3.78-.91-2.36-1.55-4.47-2.96-6.6-4.35-2.75-1.8-5.54-3.54-8.25-5.39-.97-.66-1.87-1.46-2.63-2.35-1.54-1.81-1.43-3.73,.37-5.26,1.02-.87,2.22-1.61,3.45-2.17,5.4-2.47,10.84-4.84,16.27-7.26,.91-.4,1.8-.85,2.71-1.27,2.7-1.25,3.58-3.46,3.24-6.28-.33-2.71-.82-5.41-.99-8.13-.14-2.08-.08-4.21,.22-6.27,.77-5.37-.08-10.54-1.72-15.6-3.71-11.44-7.48-22.86-11.24-34.28-3.91-11.87-7.89-23.73-11.73-35.62-2.52-7.83-.76-15.02,4.12-21.39,3.86-5.05,10.09-6.27,15.57-3.02,3.08,1.83,5.88,4.23,8.51,6.68,15,14,31.31,26.21,48.93,36.7,11.56,6.89,23.54,12.94,35.86,18.32,1.98,.86,3.57,.62,5.29-.79,2.99-2.46,6.22-4.63,9.27-7.02,6.04-4.72,10.38-10.72,13.35-17.79,.92-2.19,2.15-4.31,3.57-6.21,2.98-3.98,7.21-5.43,12.07-5.29,3.96,.12,7.7,1.25,11.46,2.34,.45,.13,.91,.26,1.47,.42,.91-3.21,1.8-6.36,2.7-9.51,1.98-6.96,4.01-13.9,7.53-20.29,1.76-3.21,3.64-6.36,6.29-8.94,3.72-3.62,8.54-4.01,12.1-1.03,1.69,1.41,2.87,3.2,3.59,5.25,3.42,9.78,6.79,19.58,10.17,29.38,.28,.8,.5,1.62,.78,2.42,.75,2.12,1.55,2.49,3.71,1.92,2.1-.56,4.2-1.17,6.34-1.51,5.13-.84,9.95-.2,14,3.46,2.51,2.27,4.23,5.1,5.64,8.14,.87,1.87,1.64,3.8,2.58,5.63,2.12,4.15,5.21,7.43,9.26,9.74,3.11,1.77,5.25,4.49,7.38,7.24,.54,.7,1.06,1.42,1.63,2.1,1.56,1.85,2.95,2.28,5.2,1.34,3.64-1.52,7.27-3.1,10.85-4.76,28.71-13.33,54.57-30.89,77.67-52.52,1.72-1.61,3.53-3.17,5.48-4.48,6.33-4.21,13.53-2.95,18.19,3.06,4.08,5.26,5.41,11.28,4.11,17.67-1.28,6.28-3.04,12.47-4.72,18.66-3.59,13.22-7.52,26.33-12.9,38.94-2.4,5.63-5.16,11.11-7.87,16.59-1.25,2.53-1.37,5.17-1.38,7.87-.03,7.69-.06,15.37-.08,23.06,0,1.96,.69,2.85,2.73,3.69,5.73,2.37,11.45,4.74,17.17,7.14,1.36,.57,2.69,1.22,3.97,1.94,.89,.5,1.76,1.1,2.51,1.8,1.6,1.51,1.78,3.27,.34,4.92-1.08,1.23-2.38,2.33-3.74,3.25-4.2,2.82-8.48,5.52-12.73,8.27-.3,.19-.59,.41-1.04,.73,3.18,.73,6.19,1.4,9.18,2.12,3.56,.85,7.03,1.97,10.26,3.76,2.78,1.54,3.3,3.82,1.04,6.03Z"/></svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,23 +0,0 @@
# Used for many other (non-commercial) purposes as well
User-agent: CCBot
Disallow: /
# For new training only
User-agent: GPTBot
Disallow: /
# Not for training, only for user requests
User-agent: ChatGPT-User
Disallow: /
# Marker for disabling Bard and Vertex AI
User-agent: Google-Extended
Disallow: /
# Speech synthesis only?
User-agent: FacebookBot
Disallow: /
# Multi-purpose, commercial uses; including LLMs
User-agent: Omgilibot
Disallow: /

14
src/app/layout.tsx Normal file
View File

@@ -0,0 +1,14 @@
import React from "react";
import { Provider } from "@/components/ui/provider";
export default function RootLayout(props: { children: React.ReactNode }) {
const { children } = props;
return (
<html suppressHydrationWarning>
<link rel="icon" href="/images/logo/favicon/favicon.ico" sizes="any" />
<body>
<Provider>{children}</Provider>
</body>
</html>
);
}

113
src/app/page.tsx Normal file
View File

@@ -0,0 +1,113 @@
import { Button, Heading, HStack, Link, Text, VStack } from "@chakra-ui/react";
import newLogo from "../../public/images/logo/New Logo (transparent).png";
import Image from "next/image";
export default function Home() {
return (
<VStack
w="100vw"
justifyContent="center"
alignItems="center"
spaceY={12}
pt="5vh"
pb="20vh"
>
<VStack spaceY={2}>
<Image height={250} src={newLogo} alt="New LCM logo" />
<Heading
lineHeight="1"
w="80vw"
as="h1"
color="cyan"
fontSize="4rem"
textAlign="center"
>
{"Lucid Creations Media Website"}
</Heading>
</VStack>
<VStack w="80vw" spaceY={2}>
<Text w="100%" textAlign="left" fontSize="lg">
{"The LCM website and eShop has been temporarily discontinued."}
</Text>
<Text w="100%" textAlign="left" fontSize="lg">
{
"Over the years there have been many external factors that interfered with my ability to dedicate time and find motivation to work on my creative passions. This includes erotica, streaming, hypno files, and more."
}
</Text>
<Text w="100%" textAlign="left" fontSize="lg">
{
"It has also interfered with my ability to learn new skills such as music creation and digital art."
}
</Text>
<Text w="100%" textAlign="left" fontSize="lg">
{
"Last year I faced a layoff and haven't been able to get back into my field. I can no longer afford the costs of hosting this website on it's current platform. The website generated no sales and thus I had to make the difficult decision to shut the site down for now."
}
</Text>
<Text w="100%" textAlign="left" fontSize="lg">
{
'I have no time frame for when or if I will reopen the eShop. I do wish to at some point, on a more modern platform. I know that the next few months will be hell for me and I will have no time to dedicate to any of this. I will have to see how things are once I start getting more free time and if I can start working on a new website, "remastering" old hypno files, and making new ones for the relaunch.'
}
</Text>
<Text w="100%" textAlign="left" fontSize="lg">
{
"Since LCM is not registered as an LLC yet I have been contemplating rebranding the company one last time. A few weeks before coming to this decision and making this page I was being pulled to shelf Kobold as Lucid's default form. I have been enjoying hearing/seeing everyone refer to me as wolf/werwolf so much that I might keep it this way. Just like when I changed my original fursona and alias it might be time to flip to a new chapter in my life."
}
</Text>
<Text w="100%" textAlign="left" fontSize="lg">
{
'For a little while I was debating using "Kobold Kid", my new vanity domain name, as the new brand, but I have a new idea. I will sit on it for a few months before I make any decisions.'
}
</Text>
<Text w="100%" textAlign="left" fontSize="lg">
{
"Another option I was considering is keeping Kobold as marketing/mascot, but I am not sure about it."
}
</Text>
</VStack>
<VStack spaceY={6} w="80vw">
<Heading as="h2" color="cyan" fontSize="2.5rem">
{"Other LCM Resources"}
</Heading>
<HStack gap="6" wrap="wrap">
<Button variant="solid" bgColor="teal" fontSize="xl">
<Link
target="_blank"
color="whiteAlpha.950"
href="https://community.lucidcreations.media/"
>
{"The Cove"}
</Link>
</Button>
<Button variant="solid" bgColor="teal" fontSize="xl">
<Link
target="_blank"
color="whiteAlpha.950"
href="https://vrchat.com/home/group/grp_781bbe4b-51ec-4025-a14f-cf23ced90507"
>
{"Dreamy Drove"}
</Link>
</Button>
</HStack>
<Heading as="h2" color="cyan" fontSize="2.5rem">
{"Lucid's Socials"}
</Heading>
<HStack gap="6" wrap="wrap">
<Button variant="solid" bgColor="teal" fontSize="xl">
<Link
target="_blank"
color="whiteAlpha.950"
href="https://koboldkid.com/"
>
{"Kobold Kid Website"}
</Link>
</Button>
</HStack>
</VStack>
</VStack>
);
}

View File

@@ -1,25 +0,0 @@
import React from "react";
import { Box, Link, Button, BoxProps, Text } from "@chakra-ui/react";
import { motion } from "framer-motion";
interface CustomButtonProps {
text: string;
link: string;
type: "primary" | "secondary" | "footer";
}
const MotionBox = motion<BoxProps>(Box);
const CustomButton = ({ text, link, type }: CustomButtonProps): JSX.Element => {
return (
<MotionBox whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }}>
<Link href={link} target="_blank" rel="noopener">
<Button variant={type}>
<Text>{text}</Text>
</Button>
</Link>
</MotionBox>
);
};
export default CustomButton;

View File

@@ -1,24 +0,0 @@
import React from "react";
import { Box, Link, Button, BoxProps, Text } from "@chakra-ui/react";
import { Icon } from "@iconify/react";
import { motion } from "framer-motion";
const MotionBox = motion<BoxProps>(Box);
const GitHub = (): JSX.Element => {
return (
<MotionBox whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }}>
<Link
href="work/lcm/LucidCreationsWebsite/src/components/buttons/KoFi.tsx"
target="_blank"
rel="noopener"
>
<Button variant="primary" leftIcon={<Icon icon="mdi:github" />}>
<Text>{"View Codebase"}</Text>
</Button>
</Link>
</MotionBox>
);
};
export default GitHub;

View File

@@ -1,24 +0,0 @@
import React from "react";
import { Box, Link, Button, BoxProps, Text } from "@chakra-ui/react";
import { Icon } from "@iconify/react";
import { motion } from "framer-motion";
const MotionBox = motion<BoxProps>(Box);
const KoFi = (): JSX.Element => {
return (
<MotionBox whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }}>
<Link
href="https://ko-fi.com/lucidcreationsmedia"
target="_blank"
rel="noopener"
>
<Button variant="kofi" leftIcon={<Icon icon="cib:ko-fi" />}>
<Text>{"Fund The App"}</Text>
</Button>
</Link>
</MotionBox>
);
};
export default KoFi;

View File

@@ -1,28 +0,0 @@
export interface LinkObj {
href?: string;
name?: string;
type: "primary" | "secondary" | "ko-fi" | "GitHub";
}
type Links = LinkObj[];
const links: Links = [
{
href: "https://docs.google.com/document/d/1y1tbTG6TYoLMEde4XHzInByyHQ0T6Aw2RF6Y4Z7Yabs",
name: "Roadmap and Progress",
type: "secondary"
},
// {
// type: "ko-fi"
// },
{
href: "https://t.me/LucidCreationsMedia",
name: "Dev Updates",
type: "secondary"
},
{
type: "GitHub"
}
];
export default links;

View File

@@ -1,77 +0,0 @@
import React from "react";
import { Box, HStack, VStack } from "@chakra-ui/react";
import CustomButton from "./Custom";
import links, { LinkObj } from "./data/links";
import KoFi from "./KoFi";
import GitHub from "./GitHub";
const Buttons = (): JSX.Element => {
return (
<Box h="auto" w="100%">
<HStack
display={{ base: "none", lg: "flex" }}
h="auto"
w="100%"
justifyContent="center"
alignContent="center"
spacing={4}
>
{links.map((link: LinkObj) => {
const { href, name, type } = link;
if (type === "primary" || type === "secondary") {
return (
<CustomButton
key={name.replaceAll(" ", "-")}
link={href}
text={name}
type={type}
/>
);
}
if (type === "ko-fi") {
return <KoFi key={type} />;
}
if (type === "GitHub") {
return <GitHub key={type} />;
}
})}
</HStack>
<VStack
display={{ base: "flex", lg: "none" }}
h="auto"
w="100%"
justifyContent="center"
alignContent="center"
spacing={4}
>
{links.map((link: LinkObj) => {
const { href, name, type } = link;
if (type === "primary" || type === "secondary") {
return (
<CustomButton
key={name.replaceAll(" ", "-")}
link={href}
text={name}
type={type}
/>
);
}
if (type === "ko-fi") {
return <KoFi key={type} />;
}
if (type === "GitHub") {
return <GitHub key={type} />;
}
})}
</VStack>
</Box>
);
};
export default Buttons;

View File

@@ -1,27 +0,0 @@
import { Button, Heading, Text, Link, VStack } from "@chakra-ui/react";
import React from "react";
const AboutProject = (): JSX.Element => {
const description = `This project and website is a replacement for the current Lucid Creations Media Website. It is going to being designed to be faster, more user friendly, and better accessible compared to Wordpress. This platform is being built on React and Next.js`;
return (
<VStack
justifyContent="center"
alignContent="center"
w="100%"
my="10"
px={{ base: "5vw", md: "15vw", lg: "20vw", xl: "30vw" }}
spacing="4"
>
<Heading w="100%">{"About This Website"}</Heading>
<Text w="100%">{description}</Text>
<Link href="htps://lucidcreations.media" target="_blank" rel="noopener">
<Button type="button" variant="secondary">
<Text>{"Visit the current website"}</Text>
</Button>
</Link>
</VStack>
);
};
export default AboutProject;

View File

@@ -1,84 +0,0 @@
import React from "react";
import Image from "next/image";
import { Flex, HStack, Text, VStack } from "@chakra-ui/react";
import BrandText from "../../theme/components/BrandText";
import { Icon } from "@iconify/react";
import mp3SVG from "../../../public/images/mp3.svg";
const WhatIMakeBanner = (): JSX.Element => {
return (
<VStack
bgColor="brand.cosmic"
w="100%"
alignContent="center"
justifyContent="center"
py="10"
px={{ base: "0", lg: "5vw", "2xl": "10vw" }}
>
<VStack w="100%" alignContent="center" pb={{ base: "10", md: "6" }}>
<BrandText type="Heading" headerLevel="h2" text={"What I Make"} />
</VStack>
<Flex
w="100%"
justifyContent="space-around"
alignContent="center"
direction={{ base: "column", md: "row" }}
gap="5rem"
>
<VStack spacing="4" w="100%">
<BrandText type="Heading" headerLevel="h3" text={"Communities"} />
<VStack spacing="4" alignContent="start" justifyContent="center">
<HStack spacing="4">
<Text fontSize="5xl">
<Icon icon="ic:baseline-discord" />
</Text>
<BrandText type="Text" size="xl" text={"Lucid's Cove"} />
</HStack>
<HStack spacing="4">
<Text fontSize="5xl">
<Icon icon="ic:baseline-telegram" />
</Text>
<BrandText type="Text" size="xl" text={"Lucid's Cove"} />
</HStack>
</VStack>
</VStack>
<VStack spacing="4" w="100%">
<BrandText type="Heading" headerLevel="h3" text={"Content"} />
<VStack spacing="4" alignContent="start" justifyContent="center">
<HStack spacing="4" w="100%">
<Text fontSize="5xl">
<Icon icon="heroicons-solid:pencil" />
</Text>
<BrandText type="Text" size="xl" text={"Erotic Stories"} />
</HStack>
<HStack spacing="4" w="100%">
<Text fontSize="5xl">
<Image height="35" width="35" src={mp3SVG} alt="MP3 Icon" />
</Text>
<BrandText type="Text" size="xl" text={"Hypno Audio Files"} />
</HStack>
</VStack>
</VStack>
<VStack spacing="4" w="100%">
<BrandText type="Heading" headerLevel="h3" text={"Streams"} />
<VStack spacing="4" alignContent="start" justifyContent="center">
<HStack spacing="4">
<Text fontSize="5xl">
<Icon icon="mdi:twitch" />
</Text>
<BrandText type="Text" size="xl" text={"LucidKobold"} />
</HStack>
<HStack spacing="4">
<Text fontSize="5xl">
<Icon icon="mdi:youtube" />
</Text>
<BrandText type="Text" size="xl" text={"LucidKobold"} />
</HStack>
</VStack>
</VStack>
</Flex>
</VStack>
);
};
export default WhatIMakeBanner;

View File

@@ -1,32 +0,0 @@
import { Heading, VStack } from "@chakra-ui/react";
import React from "react";
import WhatIMakeBanner from "./WhatIMakeBanner";
import AboutProject from "./AboutProject";
const TempHero = (): JSX.Element => {
return (
<VStack
bg="brand.content"
h="100%"
w="100%"
spacing="0"
justifyContent="center"
alignContent="center"
>
<WhatIMakeBanner />
<AboutProject />
<VStack
w="100%"
h="36.3vh"
justifyContent="space-around"
alignContent="center"
>
<Heading size="3xl" as="h2">
{"Placeholder section"}
</Heading>
</VStack>
</VStack>
);
};
export default TempHero;

View File

@@ -1,37 +0,0 @@
import React from "react";
import {
Box,
Modal,
ModalBody,
ModalContent,
ModalOverlay
} from "@chakra-ui/react";
import LoadingSpinner from "./LoadingSpinner";
const LoadingOverlay = (): JSX.Element => {
return (
<Modal
isCentered
isOpen
onClose={() => null}
motionPreset="slideInBottom"
scrollBehavior="inside"
size="xs"
>
<ModalOverlay bg="loading.overlayBg" />
<ModalContent bg="transparent" boxShadow="none">
{/* <ModalHeader>
</ModalHeader> */}
<ModalBody border="0px">
<Box h="100%" w="100%" textAlign="center">
<LoadingSpinner />
</Box>
</ModalBody>
{/* <ModalFooter>
</ModalFooter> */}
</ModalContent>
</Modal>
);
};
export default LoadingOverlay;

View File

@@ -1,16 +0,0 @@
import React from "react";
import { Spinner } from "@chakra-ui/react";
const LoadingSpinner = (): JSX.Element => {
return (
<Spinner
thickness="4px"
speed="0.50s"
emptyColor="loading.spinnerEmptySpace"
color="loading.spinnerColor"
size="xl"
/>
);
};
export default LoadingSpinner;

View File

@@ -0,0 +1,111 @@
/* eslint-disable @typescript-eslint/no-empty-object-type */
/* eslint-disable prettier/prettier */
/* eslint-disable no-unused-vars */
"use client";
import type { IconButtonProps, SpanProps } from "@chakra-ui/react";
import { ClientOnly, IconButton, Skeleton, Span } from "@chakra-ui/react";
import { ThemeProvider, useTheme } from "next-themes";
import type { ThemeProviderProps } from "next-themes";
import * as React from "react";
import { LuMoon, LuSun } from "react-icons/lu";
export interface ColorModeProviderProps extends ThemeProviderProps { }
export function ColorModeProvider(props: ColorModeProviderProps) {
return (
<ThemeProvider attribute="class" disableTransitionOnChange {...props} />
);
}
export type ColorMode = "light" | "dark";
export interface UseColorModeReturn {
colorMode: ColorMode;
setColorMode: (colorMode: ColorMode) => void;
toggleColorMode: () => void;
}
export function useColorMode(): UseColorModeReturn {
const { resolvedTheme, setTheme, forcedTheme } = useTheme();
const colorMode = forcedTheme || resolvedTheme;
const toggleColorMode = () => {
setTheme(resolvedTheme === "dark" ? "light" : "dark");
};
return {
colorMode: colorMode as ColorMode,
setColorMode: setTheme,
toggleColorMode
};
}
export function useColorModeValue<T>(light: T, dark: T) {
const { colorMode } = useColorMode();
return colorMode === "dark" ? dark : light;
}
export function ColorModeIcon() {
const { colorMode } = useColorMode();
return colorMode === "dark" ? <LuMoon /> : <LuSun />;
}
interface ColorModeButtonProps extends Omit<IconButtonProps, "aria-label"> { }
export const ColorModeButton = React.forwardRef<
HTMLButtonElement,
ColorModeButtonProps
>(function ColorModeButton(props, ref) {
const { toggleColorMode } = useColorMode();
return (
<ClientOnly fallback={<Skeleton boxSize="8" />}>
<IconButton
onClick={toggleColorMode}
variant="ghost"
aria-label="Toggle color mode"
size="sm"
ref={ref}
{...props}
css={{
_icon: {
width: "5",
height: "5"
}
}}
>
<ColorModeIcon />
</IconButton>
</ClientOnly>
);
});
export const LightMode = React.forwardRef<HTMLSpanElement, SpanProps>(
function LightMode(props, ref) {
return (
<Span
color="fg"
display="contents"
className="chakra-theme light"
colorPalette="gray"
colorScheme="light"
ref={ref}
{...props}
/>
);
}
);
export const DarkMode = React.forwardRef<HTMLSpanElement, SpanProps>(
function DarkMode(props, ref) {
return (
<Span
color="fg"
display="contents"
className="chakra-theme dark"
colorPalette="gray"
colorScheme="dark"
ref={ref}
{...props}
/>
);
}
);

View File

@@ -0,0 +1,12 @@
"use client";
import { ChakraProvider, defaultSystem } from "@chakra-ui/react";
import { ColorModeProvider, type ColorModeProviderProps } from "./color-mode";
export function Provider(props: ColorModeProviderProps) {
return (
<ChakraProvider value={defaultSystem}>
<ColorModeProvider {...props} />
</ChakraProvider>
);
}

View File

@@ -0,0 +1,43 @@
"use client";
import {
Toaster as ChakraToaster,
Portal,
Spinner,
Stack,
Toast,
createToaster
} from "@chakra-ui/react";
export const toaster = createToaster({
placement: "bottom-end",
pauseOnPageIdle: true
});
export const Toaster = () => {
return (
<Portal>
<ChakraToaster toaster={toaster} insetInline={{ mdDown: "4" }}>
{(toast) => (
<Toast.Root width={{ md: "sm" }}>
{toast.type === "loading" ? (
<Spinner size="sm" color="blue.solid" />
) : (
<Toast.Indicator />
)}
<Stack gap="1" flex="1" maxWidth="100%">
{toast.title && <Toast.Title>{toast.title}</Toast.Title>}
{toast.description && (
<Toast.Description>{toast.description}</Toast.Description>
)}
</Stack>
{toast.action && (
<Toast.ActionTrigger>{toast.action.label}</Toast.ActionTrigger>
)}
{toast.closable && <Toast.CloseTrigger />}
</Toast.Root>
)}
</ChakraToaster>
</Portal>
);
};

View File

@@ -0,0 +1,46 @@
import { Tooltip as ChakraTooltip, Portal } from "@chakra-ui/react";
import * as React from "react";
export interface TooltipProps extends ChakraTooltip.RootProps {
showArrow?: boolean;
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>;
content: React.ReactNode;
contentProps?: ChakraTooltip.ContentProps;
disabled?: boolean;
}
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
function Tooltip(props, ref) {
const {
showArrow,
children,
disabled,
portalled = true,
content,
contentProps,
portalRef,
...rest
} = props;
if (disabled) return children;
return (
<ChakraTooltip.Root {...rest}>
<ChakraTooltip.Trigger asChild>{children}</ChakraTooltip.Trigger>
<Portal disabled={!portalled} container={portalRef}>
<ChakraTooltip.Positioner>
<ChakraTooltip.Content ref={ref} {...contentProps}>
{showArrow && (
<ChakraTooltip.Arrow>
<ChakraTooltip.ArrowTip />
</ChakraTooltip.Arrow>
)}
{content}
</ChakraTooltip.Content>
</ChakraTooltip.Positioner>
</Portal>
</ChakraTooltip.Root>
);
}
);

View File

@@ -1,66 +0,0 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { format } from "date-fns";
import populate from "../../../lib/populateMonth";
interface CalenderSlice {
currDate: string;
selectedDateInfo: SelectedDateInfo;
isLoading: boolean;
}
const getCurrDate = (): Date => new Date();
const dateParse = (date: Date) => date.toJSON();
const dateFormatter = (date: Date): string => format(date, "LLLL uuuu");
const initialState: CalenderSlice = {
currDate: dateParse(getCurrDate()),
selectedDateInfo: {
date: dateParse(getCurrDate()),
title: dateFormatter(getCurrDate()),
layout: populate(getCurrDate())
},
isLoading: true
};
// TODO: Add a function that validated if a month has at least one sticker in it. Use that within the nav function (when filter is enabled).
// TODO: Add a function that will give the closest date, if available, when the nav func detects an empty month.
// Use the chart creation date to aid with this. (When filter is enabled)
/**
* TODO: Add logic that prevents navigation to the future and too far in the past. (Use chart creation date)
* Update to use a promise and return appropriate errors. Display those errors on the front end.
* Update the use of this function on the front to handle the fails of the promise.
*/
// TODO: (When filter is enabled) Update the calender update function that will take in a direction so that the the navigation buttons will take the user to the next month with stickers. Assuming there was a gap with empty months.
const calenderSlice = createSlice({
name: "Calender",
initialState,
reducers: {
// Update month info
updateMonth(state: CalenderSlice, action: PayloadAction<string>) {
const { payload } = action;
const toDateObj: Date = new Date(payload);
state.selectedDateInfo.date = payload;
state.selectedDateInfo.title = dateFormatter(toDateObj);
state.selectedDateInfo.layout = populate(toDateObj);
},
// Update current date
updateCurrDate(state: CalenderSlice) {
state.currDate = dateParse(new Date());
},
// Update isLoading
updateLoading(state: CalenderSlice, action: PayloadAction<boolean>) {
const { payload } = action;
state.isLoading = payload;
}
}
});
export const { updateMonth, updateCurrDate, updateLoading } =
calenderSlice.actions;
export default calenderSlice.reducer;

View File

@@ -1,62 +0,0 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { format, getDate, isSameDay } from "date-fns";
import stickersSeeder from "../../../data/stickerSeeder";
interface StickersSlice {
stickersMonth: StickerDays;
}
interface UpdateStickerSlicePayload {
stickerDate: string;
sticker: StickerVal;
}
const initialState: StickersSlice = {
stickersMonth: stickersSeeder()
};
const stickersSlice = createSlice({
name: "Stickers",
initialState,
reducers: {
addEditSticker(
state: StickersSlice,
actions: PayloadAction<UpdateStickerSlicePayload>
) {
const { stickerDate, sticker } = actions.payload;
const dateObj = new Date(stickerDate);
// Getting index for the stickers array, sticker from the stickers array, and the date from the sticker.
const index: number = getDate(dateObj) - 1;
const currSticker: Sticker = state.stickersMonth[index];
// Updating the edited status by checking if the sticker date is today's date.
const edited = currSticker.edited
? true
: isSameDay(new Date(stickerDate), new Date())
? false
: true;
currSticker.edited = edited;
// TODO: Add manually added here.
// Updating the id of the sticker.
const id = format(dateObj, "yyyyddLL") + sticker;
// Updating the information of the sticker.
const newSticker: Sticker = {
id: id,
date: stickerDate,
sticker: sticker,
edited: edited,
manual: false
};
state.stickersMonth[index] = newSticker;
}
}
});
export const { addEditSticker } = stickersSlice.actions;
export default stickersSlice.reducer;

View File

@@ -1,140 +0,0 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { addMonths, endOfDay } from "date-fns";
import versionStringToNumber from "../../../lib/versionStringToNumber";
export interface StorageState {
exp: string;
version: number;
completed: boolean;
}
const endOfToday: Date = endOfDay(new Date());
const generateExpDate = (): string => {
return endOfDay(addMonths(endOfToday, 1)).toJSON();
};
const generateVersion = (): number => {
const versionStr: string = process.env.NEXT_PUBLIC_APP_VERSION;
return versionStringToNumber(versionStr);
};
// * Storage Helpers * //
const setTempStorage = (storageState: StorageState): void => {
sessionStorage.setItem("completedTutorial", JSON.stringify(storageState));
};
const getTempStorage = (): StorageState | null => {
return JSON.parse(sessionStorage.getItem("completedTutorial"));
};
const clearTempStorage = (): void => {
sessionStorage.removeItem("completedTutorial");
};
const setStorage = (storageState: StorageState): void => {
localStorage.setItem("completedTutorial", JSON.stringify(storageState));
};
const getStorage = (): StorageState | null => {
return JSON.parse(localStorage.getItem("completedTutorial"));
};
const clearStorage = (): void => {
localStorage.removeItem("completedTutorial");
};
interface TutorialSlice {
completedTutorial: boolean | null;
storageState: StorageState | null;
rememberCompleted: boolean;
currWeek: MonthDay[] | null;
}
const initialState: TutorialSlice = {
completedTutorial: null,
storageState: null,
rememberCompleted: false,
currWeek: null
};
const tutorialSlice = createSlice({
name: "Tutorial",
initialState,
reducers: {
// Set temp complete
setTempTutorialComplete(state: TutorialSlice) {
const exp: string = generateExpDate();
const version: number = generateVersion();
const storageState: StorageState = {
exp,
version,
completed: true
};
setTempStorage(storageState);
state.storageState = storageState;
state.completedTutorial = true;
},
// Set completed (remember)
setTutorialCompleted(state: TutorialSlice) {
const exp: string = generateExpDate();
const version: number = generateVersion();
const storageState: StorageState = {
exp,
version,
completed: true
};
setStorage(storageState);
state.storageState = storageState;
state.completedTutorial = true;
},
// Clear states and storages
clearTutorialCompleted(state: TutorialSlice) {
clearTempStorage();
clearStorage();
state.storageState = null;
state.completedTutorial = null;
},
// Get and set states
getAndSetTutorial(state: TutorialSlice) {
const temp = getTempStorage();
const local = getStorage();
if (temp !== null || local !== null) {
state.storageState = temp !== null ? temp : local;
state.completedTutorial =
temp !== null ? temp.completed : local.completed;
}
if (temp === null && local === null) {
state.completedTutorial = false;
}
},
// Toggle remember completed
toggleRememberCompleted(state: TutorialSlice) {
const { rememberCompleted } = state;
state.rememberCompleted = !rememberCompleted;
},
// Set current week
setCurrentWeek(state: TutorialSlice, action: PayloadAction<MonthDay[]>) {
const { payload } = action;
state.currWeek = payload;
}
}
});
export const {
setTempTutorialComplete,
setTutorialCompleted,
clearTutorialCompleted,
getAndSetTutorial,
toggleRememberCompleted,
setCurrentWeek
} = tutorialSlice.actions;
export default tutorialSlice.reducer;

View File

@@ -1,37 +0,0 @@
import "@fontsource/montserrat/500.css";
import "@fontsource/tilt-neon/400.css";
import "@fontsource/anonymous-pro/400.css";
import "@fontsource/kalam/400.css";
import "@fontsource/anybody/400.css";
import type { AppProps } from "next/app";
import React from "react";
import { ChakraProvider } from "@chakra-ui/react";
import AppTheme from "../theme/AppTheme";
import { Provider } from "react-redux";
import { store } from "../redux/store";
import Layout from "../theme/layout/Layout";
import Head from "next/head";
function LCMPottyChart({ Component, pageProps }: AppProps): JSX.Element {
return (
<React.StrictMode>
<ChakraProvider theme={AppTheme}>
<Layout {...pageProps}>
<Head>
<title>{"LCM Website"}</title>
<meta
name="viewport"
content="width=device-width, user-scalable=yes, initial-scale=1.0"
/>
</Head>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</Layout>
</ChakraProvider>
</React.StrictMode>
);
}
export default LCMPottyChart;

View File

@@ -1,56 +0,0 @@
import NextDocument, { Html, Head, Main, NextScript } from "next/document";
import React from "react";
import { ColorModeScript } from "@chakra-ui/react";
import AppTheme from "../theme/AppTheme";
const description =
"Official website for Lucid Creations Media. Where you can back us, donate to us, and purchase our official content such as hypno audio files.";
const logo = "images/logo.svg";
const logoOG = "/images/logo.png";
class Document extends NextDocument {
render(): JSX.Element {
return (
<Html>
<Head>
<meta name="theme-color" content="#3138dc" />
<link rel="icon" href={logo} sizes="32x32 192x192" />
<link rel="apple-touch-icon" href={logo} />
<meta property="og:title" content="Lucid Creations Media Website" />
<meta name="og:description" content={description} />
<meta property="og:type" content="eCommerce" />
<meta property="og:image" content={logoOG} />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:alt" content="Lucid Creations Media Logo" />
<meta property="og:url" content="https://lucidcreations.media" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="title" content="Lucid Creations Media Website" />
<meta name="description" content={description} />
<meta property="type" content="eCommerce" />
<meta property="image" content={logoOG} />
<meta property="image:type" content="image/png" />
<meta property="image:alt" content="Lucid Creations Media Logo" />
<meta property="url" content="https://https://lucidcreations.media" />
<meta httpEquiv="content-language" content="en_US" />
<meta charSet="UTF-8" />
<meta name="keywords" content={description} />
<meta name="copyright" content="Lucid Creations Media" />
<meta name="page-topic" content="eCommerce" />
<meta name="audience" content="E" />
<meta name="robots" content="index, follow" />
</Head>
<html lang="en" />
<body>
<ColorModeScript
initialColorMode={AppTheme.config.initialColorMode}
/>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default Document;

View File

@@ -1,17 +0,0 @@
import React from "react";
import { Provider } from "react-redux";
import { store } from "../redux/store";
import { Box } from "@chakra-ui/react";
import TempHero from "../components/hero";
const IndexPage = (): JSX.Element => {
return (
<Box textAlign="center" w="100%" h="auto" pt="50px" minWidth="min-content">
<Provider store={store}>
<TempHero />
</Provider>
</Box>
);
};
export default IndexPage;

View File

@@ -1,5 +0,0 @@
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { RootState, AppDispatch } from "./store";
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

View File

@@ -1,15 +0,0 @@
import { configureStore } from "@reduxjs/toolkit";
import calenderReducer from "../features/calender";
import stickersReducer from "../features/calender/stickers";
import tutorialReducer from "../features/tutorial";
export const store = configureStore({
reducer: {
calender: calenderReducer,
stickers: stickersReducer,
tutorial: tutorialReducer
}
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;

View File

@@ -1,69 +0,0 @@
import { extendTheme, ThemeConfig } from "@chakra-ui/react";
// import { createBreakpoints } from "@chakra-ui/theme-tools";
import "@fontsource/montserrat";
import "@fontsource/tilt-neon";
import "@fontsource/anonymous-pro";
import "@fontsource/kalam";
import "@fontsource/anybody";
import buttons from "./components/buttonStyles";
const config: ThemeConfig = {
initialColorMode: "dark",
useSystemColorMode: false
};
// const breakpoints = createBreakpoints({
// sm: "30em",
// md: "48em",
// lg: "75em",
// xl: "85em",
// "2xl": "100em",
// });
const fonts = {
heading: `'Tilt Neon', system-ui`,
body: `'Montserrat', sans-serif`,
mono: `'Anonymous Pro', monospace`,
brand: `'Kalam', cursive`,
LCM: `'Anybody', system-ui`
};
const AppTheme = extendTheme({
config,
colors: {
brand: {
main: "#3138dc",
primary: "#0068ff",
secondary: "#0086ff",
cosmic: "#314a9e",
hover: "#00aec1",
warning: "#ffbd48",
danger: "#FC8181",
valid: "#00c17c",
footer: "#0097a7",
footerText: "black",
content: "#2d3748",
kofi: "#FF5E5B"
},
loading: {
overlayBg: "#171923cb",
spinnerColor: "#0088ff",
spinnerEmptySpace: "#2D374860"
}
},
styles: {
global: {
body: {
bg: "gray.900"
}
}
},
components: {
Button: buttons
},
fonts
// breakpoints,
});
export default AppTheme;
export { fonts };

View File

@@ -1,29 +0,0 @@
import { Heading, Text } from "@chakra-ui/react";
import React from "react";
import { fonts } from "../AppTheme";
interface BrandTextProps {
type: "Heading" | "Text";
text: string;
headerLevel?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
size?: string;
}
const BrandText = ({
type,
text,
headerLevel,
size
}: BrandTextProps): JSX.Element => {
return type === "Heading" ? (
<Heading fontFamily={fonts.brand} fontSize={size} as={headerLevel}>
{text}
</Heading>
) : (
<Text fontFamily={fonts.brand} fontSize={size}>
{text}
</Text>
);
};
export default BrandText;

View File

@@ -1,177 +0,0 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { darken, mode, whiten } from "@chakra-ui/theme-tools";
import { Dict } from "@chakra-ui/utils";
const buttonStyles = {
// style object for base or default style
baseStyle: {},
// styles for different sizes ("sm", "md", "lg")
sizes: {},
// styles for different visual variants ("outline", "solid")
variants: {
primary: (props: Dict<never>) => ({
bg: "brand.primary",
fontSize: "xl",
py: 3,
px: 4,
color: "whiteAlpha",
_hover: {
bg: mode(
whiten("brand.primary", 20),
darken("brand.primary", 20)
)(props)
}
}),
secondary: (props: Dict<never>) => ({
bg: "brand.secondary",
fontSize: "xl",
py: 3,
px: 4,
color: "whiteAlpha",
_hover: {
bg: mode(
whiten("brand.secondary", 20),
darken("brand.secondary", 20)
)(props)
}
}),
skip: (props: Dict<never>) => ({
bg: "transparent",
fontSize: "xl",
py: 3,
px: 4,
color: "whiteAlpha.800",
_hover: {
bg: mode(whiten("brand.danger", 20), darken("brand.danger", 20))(props),
color: "whiteAlpha.900"
}
}),
stickerButton: (props: Dict<never>) => ({
bg: "transparent",
fontSize: "4rem",
px: 2,
py: 14,
_hover: {
bg: mode(
whiten("brand.secondary", 20),
darken("brand.secondary", 20)
)(props)
}
}),
nav: (props: Dict<never>) => ({
bg: "transparent",
fontSize: "md",
px: 2,
_hover: {
bg: mode(
whiten("brand.secondary", 20),
darken("brand.secondary", 20)
)(props)
}
}),
stickyNav: (/* props: Dict<never> | StyleFunctionProps */) => ({
bg: "transparent",
fontSize: "md",
px: 2,
_hover: {
textDecoration: "underline"
}
}),
footer: (props: Dict<never>) => ({
bg: "brand.main",
fontSize: "lg",
py: 3,
px: 4,
color: "whiteAlpha",
_hover: {
bg: mode(whiten("brand.main", 20), darken("brand.main", 20))(props)
}
}),
backToTop: (props: Dict<never>) => ({
bg: "rgba(23, 25, 35, 0.5)",
fontSize: "lg",
py: 2,
px: 4,
color: "rgba(0, 134, 255, 0.6)",
boxShadow:
"rgba(0, 134, 255, 0.05) 0px 0px 15px, rgba(0, 134, 255, 0.1) 0px 0px 3px 1px",
border: "1px solid rgba(0, 134, 255, 0.15)",
_hover: {
bg: mode(
whiten("brand.secondary", 20),
darken("brand.secondary", 20)
)(props),
boxShadow:
"rgba(0, 104, 255, 0.5) 0px 0px 15px, rgba(0, 104, 255, 0.3) 0px 0px 3px 1px",
color: "whiteAlpha.900",
border: "1px solid rgba(0, 134, 255, 1)"
}
}),
submit: (props: Dict<never>) => ({
fontSize: "lg",
py: 2,
px: 4,
type: "submit",
_hover: {
color: "whiteAlpha.900",
bg: mode(whiten("brand.valid", 20), darken("brand.valid", 20))(props),
_disabled: {
color: mode(
whiten("brand.danger", 20),
darken("brand.danger", 20)
)(props),
boxShadow:
"rgba(252, 129, 129, .95) 0px 0px 15px, rgba(252, 129, 129, 0.75) 0px 0px 3px 1px",
border: "1px solid #FC8181"
}
}
}),
mobileNav: (props: Dict<never>) => ({
// bg: "transparent",
fontSize: "md",
px: 2,
boxShadow:
"rgba(0, 134, 255, 0.30) 0px 0px 15px, rgba(0, 134, 255, 0.15) 0px 0px 3px 1px",
_hover: {
bg: mode(
whiten("brand.secondary", 20),
darken("brand.secondary", 20)
)(props),
boxShadow:
"rgba(0, 134, 255, 0.5) 0px 0px 15px, rgba(0, 134, 255, 0.3) 0px 0px 3px 1px"
},
_expanded: {
bg: "brand.primary",
boxShadow:
"rgba(0, 134, 255, 0.5) 0px 0px 15px, rgba(0, 134, 255, 0.3) 0px 0px 3px 1px",
border: "1px solid #0068ff"
}
}),
kofi: (props: Dict<never>) => ({
bg: "brand.kofi",
fontSize: "lg",
p: 3,
color: "whiteAlpha",
_hover: {
bg: mode(whiten("brand.kofi", 20), darken("brand.kofi", 20))(props)
}
}),
twitter: (props: Dict<never>) => ({
bg: "brand.twitter",
fontSize: "lg",
py: 3,
px: 4,
color: "whiteAlpha",
_hover: {
bg: mode(
whiten("brand.twitter", 20),
darken("brand.twitter", 20)
)(props)
}
})
},
// default values for `size` and `variant`
defaultProps: {}
};
export default buttonStyles;

View File

@@ -1,32 +0,0 @@
import React, { FC } from "react";
import { Button, Flex, Link } from "@chakra-ui/react";
import { Icon } from "@iconify/react";
interface BackToTopButtonProps {
show: boolean;
}
const BackToTopButton: FC<BackToTopButtonProps> = ({
show
}: BackToTopButtonProps) => {
return (
<Flex
display={show ? "flex" : "none"}
pos="fixed"
top="85vh"
right={{
base: "1.25rem",
sm: "2rem",
md: "3rem"
}}
>
<Link href="/#top">
<Button variant="backToTop">
<Icon icon="akar-icons:chevron-up" />
</Button>
</Link>
</Flex>
);
};
export default BackToTopButton;

View File

@@ -1,31 +0,0 @@
import React from "react";
import { Button, HStack, Link, Text } from "@chakra-ui/react";
import navItems, { NavItem } from "./navItems";
const DesktopNav = (): JSX.Element => {
return (
<HStack
as="nav"
display={{ base: "none", lg: "flex" }}
h="auto"
w="auto"
spacing={4}
// m="auto"
justifyContent="center"
alignContent="center"
alignItems="center"
>
{navItems.map((navItem: NavItem) => {
return (
<Link id={"dekstop-" + navItem[0]} key={navItem[0]} href={navItem[1]}>
<Button variant="nav">
<Text>{navItem[0]}</Text>
</Button>
</Link>
);
})}
</HStack>
);
};
export default DesktopNav;

View File

@@ -1,37 +0,0 @@
import React /*, { useEffect, useRef, useState }*/ from "react";
import { Box, Text, VStack, Link } from "@chakra-ui/react";
// import BackToTopButton from "./BackToTopButton";
import Buttons from "../../components/buttons";
const Footer = (): JSX.Element => {
return (
<Box bg="brand.footer" as="footer" w="100%" h="auto">
{/* <BackToTopButton show={showBackToTop} /> */}
<VStack
h="auto"
w="auto"
py={12}
spacing={5}
justifyItems="center"
justifyContent="center"
>
<VStack spacing={4}>
<Buttons />
<Text color="brand.footerText" fontSize="xs">
&copy;
{` 2021 - ${new Date().getFullYear()} `}
<Link
href="https://lucidcreations.media"
rel="noopener"
target="_blank"
>
{"Lucid Creations Media"}
</Link>
</Text>
</VStack>
</VStack>
</Box>
);
};
export default Footer;

View File

@@ -1,195 +0,0 @@
import React, { useEffect, useRef, useState } from "react";
import Image from "next/image";
import {
Heading,
HStack,
Box,
IconButton,
Menu,
MenuButton
} from "@chakra-ui/react";
import { Icon } from "@iconify/react";
import DesktopNav from "./DesktopNav";
import MobileNav from "./MobileNav";
import appLogo from "../../../public/images/logo.svg";
import { fonts } from "../AppTheme";
const Header = (): JSX.Element => {
const appName = "Lucid Creations Media";
const appVersion = process.env.NEXT_PUBLIC_APP_VERSION_HEADER || "";
// Add transparency while not at the top of the page.
const [transparentNavbar, setTransparentNavbar] = useState<boolean>(false);
const lastScroll = useRef<number>(0);
const handleScroll = (): void => {
// Sticky Nav
if (window.scrollY >= 20) {
setTransparentNavbar(true);
} else {
setTransparentNavbar(false);
}
// Scroll Position.
const currentScroll =
window.scrollY || window.pageYOffset || document.body.scrollTop;
// Update Scroll Position Reference
lastScroll.current = currentScroll <= 0 ? 0 : currentScroll;
// setScroll(lastScroll.current = currentScroll <= 0 ? 0 : currentScroll)
};
useEffect(() => {
if (!window) {
console.log("waiting for mount");
} else if (window) {
window.addEventListener("scroll", handleScroll);
}
return () => window.removeEventListener("scroll", handleScroll);
}, []);
// Mobile Menu Icon && Open/Close
const [open, setOpen] = useState<boolean>(false);
const [hover, setHover] = useState<boolean>(false);
const menuIcon = (): JSX.Element => {
const iconType = {
default: <Icon icon="bx:bx-menu-alt-right" />,
hover: <Icon icon="bx:bx-menu" />,
open: <Icon icon="bx:bx-x" />
};
if (open) {
return iconType.open;
} else if (hover) {
return iconType.hover;
} else {
return iconType.default;
}
};
return (
<Box
zIndex={1}
w="100%"
pos="fixed"
top={0}
alignItems="center"
boxShadow={
open
? "none"
: "rgba(0, 134, 255, 0.75) 0px 0px 15px, rgba(0, 134, 255, 0.5) 0px 0px 3px 1px"
}
bg={
open
? "brand.main"
: transparentNavbar
? "rgba(49, 56, 220, 0.9)"
: "brand.main"
}
transition=".5s ease"
borderRadius="0px 0px 10px 10px"
_hover={{
bg: "brand.main",
boxShadow: open
? "none"
: "rgba(0, 134, 255, 0.9) 0px 0px 15px, rgba(0, 134, 255, 0.7) 0px 0px 3px 1px"
}}
h={open ? "125px" : "auto"}
>
{/* Logo | Site Name */}
<HStack
display={{ base: "flex", lg: "none" }}
position="absolute"
width="100%"
height={12}
top={0}
ml={4}
spacing="5px"
justifyContent={{
base: "flex-start",
sm: "center"
}}
alignItems="center"
_hover={{
cursor: "default"
}}
>
<Image height="30" width="30" src={appLogo} alt="App Logo" />
<Heading as="h1" size="md" fontFamily={fonts.LCM} fontWeight="400">
{appName}
</Heading>
<Heading color="whiteAlpha.500" as="h2" size="sm">
{appVersion}
</Heading>
</HStack>
{/* Desktop Nav Items and Mobile Menu Button */}
<HStack
w="100%"
px={4}
h={12}
alignItems="center"
justifyContent="space-between"
>
<HStack
w="100%"
h="auto"
alignItems="center"
justifyContent="space-between"
>
<Box w="auto" display={{ base: "flex", lg: "none " }}></Box>
<Box w="100%" display={{ base: "none", lg: "flex" }} m="auto">
<HStack
width="100%"
alignItems="center"
height="auto"
spacing="5px"
_hover={{
cursor: "default"
}}
>
<Image height="30" width="30" src={appLogo} alt="App Logo" />
<Heading
as="h1"
size="md"
fontFamily={fonts.LCM}
fontWeight="400"
>
{appName}
</Heading>
<Heading color="whiteAlpha.500" as="h2" size="sm">
{appVersion}
</Heading>
</HStack>
</Box>
<DesktopNav />
</HStack>
<Menu isLazy lazyBehavior="unmount" isOpen={open}>
<MenuButton
id="mobile-menu-button"
as={IconButton}
aria-label="Mobile Menu"
icon={menuIcon()}
display={{
base: "inline-flex",
lg: "none"
}}
bg={transparentNavbar ? "transparent" : "rgba(255, 255, 255, .15)"}
border={transparentNavbar ? "1px solid #0068ff" : "none"}
variant="mobileNav"
type="button"
onClick={() => setOpen(!open)}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
/>
<MobileNav updateOpen={setOpen} />
</Menu>
</HStack>
</Box>
);
};
export default Header;

View File

@@ -1,24 +0,0 @@
import React, { FC, ReactNode } from "react";
import type { AppProps } from "next/app";
import Header from "../layout/Header";
import { Box } from "@chakra-ui/layout";
import Footer from "./Footer";
interface LayoutProps {
children: ReactNode;
elementType?: string;
}
const Layout: FC<LayoutProps> = (
{ children }: LayoutProps,
{ pageProps }: AppProps
) => {
return (
<Box w="100%">
<Header {...pageProps} />
<main>{children}</main>
<Footer />
</Box>
);
};
export default Layout;

View File

@@ -1,57 +0,0 @@
import React, { FC, Fragment } from "react";
import {
Button,
Link,
MenuDivider,
MenuItem,
MenuList,
Text
} from "@chakra-ui/react";
import navItems, { NavItem } from "./navItems";
interface MobileNavProps {
updateOpen: React.Dispatch<React.SetStateAction<boolean>>;
}
const MobileNav: FC<MobileNavProps> = ({ updateOpen }: MobileNavProps) => {
return (
<MenuList
as="nav"
display={{ base: "block", lg: "none" }}
h="auto"
w="100%"
p={0}
border="none"
boxShadow="none"
bg="brand.main"
>
{navItems.map((navItem: NavItem, index: number) => {
return (
<MenuItem
id={"mobile-" + navItem[0]}
key={navItem[0]}
w="auto"
h="auto"
p={0}
_hover={{
backgroundColor: "none"
}}
_focus={{
backgroundColor: "none"
}}
>
<Link onClick={() => updateOpen(false)} href={navItem[1]}>
{index === 0 ? <MenuDivider /> : <Fragment></Fragment>}
<Button w="100vw" variant={"nav"} p={0} m="auto">
<Text>{navItem[0]}</Text>
</Button>
<MenuDivider />
</Link>
</MenuItem>
);
})}
</MenuList>
);
};
export default MobileNav;

View File

@@ -1,6 +0,0 @@
export type NavItem = [string, string];
export type NavItems = NavItem[];
const navItems: NavItems = [["Home", "/"]];
export default navItems;

View File

@@ -1,30 +1,27 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"target": "ESNext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

56
types/Calender.d.ts vendored
View File

@@ -1,56 +0,0 @@
type Days =
| "Sunday"
| "Monday"
| "Tuesday"
| "Wednesday"
| "Thursday"
| "Friday"
| "Saturday";
type DaysOfWeek = Days[];
interface WeekDays {
sunday: DaysOfWeek;
monday: DaysOfWeek;
}
interface MonthDay {
date: string;
isOverflow: boolean;
overflowDirection: "prev" | "next" | null;
}
interface Month {
week1: MonthDay[];
week2: MonthDay[];
week3: MonthDay[];
week4: MonthDay[];
week5: MonthDay[];
week6: MonthDay[];
}
interface WeekLayout {
weekdays: DaysOfWeek;
month: Month;
}
interface MonthLayout {
sunday: WeekLayout;
monday: WeekLayout;
}
interface UpdateCalenderPropsDateLayout {
year: number;
month: number;
day: number;
}
interface UpdateCalendarProps {
date: UpdateCalenderPropsDateLayout;
isLoading: boolean;
}
interface SelectedDateInfo {
date: string;
title: string;
layout: MonthLayout;
}

24
types/Stickers.d.ts vendored
View File

@@ -1,24 +0,0 @@
type StickerVal = -2 | -1 | 0 | 1 | 2 | null;
type ValidStickerVal = -2 | -1 | 0 | 1 | 2;
interface AddEditStickerProps {
date: Date;
sticker: ValidStickerVal;
}
interface Sticker {
id: string;
date: string;
sticker: StickerVal;
edited: boolean;
manual: boolean;
}
type StickerDays = Sticker[];
interface StickerModal {
isOpen: boolean;
selectedSticker: StickerVal;
step: number;
}

139
types/cache-life.d.ts vendored Normal file
View File

@@ -0,0 +1,139 @@
// Type definitions for Next.js cacheLife configs
declare module "next/cache" {
export { unstable_cache } from "next/dist/server/web/spec-extension/unstable-cache";
export {
revalidateTag,
revalidatePath,
unstable_expireTag,
unstable_expirePath
} from "next/dist/server/web/spec-extension/revalidate";
export { unstable_noStore } from "next/dist/server/web/spec-extension/unstable-no-store";
/**
* Cache this `"use cache"` for a timespan defined by the `"default"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 900 seconds (15 minutes)
* expire: never
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 15 minutes, start revalidating new values in the background.
* It lives for the maximum age of the server cache. If this entry has no traffic for a while, it may serve an old value the next request.
*/
export function unstable_cacheLife(profile: "default"): void;
/**
* Cache this `"use cache"` for a timespan defined by the `"seconds"` profile.
* ```
* stale: 0 seconds
* revalidate: 1 seconds
* expire: 60 seconds (1 minute)
* ```
*
* This cache may be stale on clients for 0 seconds before checking with the server.
* If the server receives a new request after 1 seconds, start revalidating new values in the background.
* If this entry has no traffic for 1 minute it will expire. The next request will recompute it.
*/
export function unstable_cacheLife(profile: "seconds"): void;
/**
* Cache this `"use cache"` for a timespan defined by the `"minutes"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 60 seconds (1 minute)
* expire: 3600 seconds (1 hour)
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 1 minute, start revalidating new values in the background.
* If this entry has no traffic for 1 hour it will expire. The next request will recompute it.
*/
export function unstable_cacheLife(profile: "minutes"): void;
/**
* Cache this `"use cache"` for a timespan defined by the `"hours"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 3600 seconds (1 hour)
* expire: 86400 seconds (1 day)
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 1 hour, start revalidating new values in the background.
* If this entry has no traffic for 1 day it will expire. The next request will recompute it.
*/
export function unstable_cacheLife(profile: "hours"): void;
/**
* Cache this `"use cache"` for a timespan defined by the `"days"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 86400 seconds (1 day)
* expire: 604800 seconds (1 week)
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 1 day, start revalidating new values in the background.
* If this entry has no traffic for 1 week it will expire. The next request will recompute it.
*/
export function unstable_cacheLife(profile: "days"): void;
/**
* Cache this `"use cache"` for a timespan defined by the `"weeks"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 604800 seconds (1 week)
* expire: 2592000 seconds (30 days)
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 1 week, start revalidating new values in the background.
* If this entry has no traffic for 30 days it will expire. The next request will recompute it.
*/
export function unstable_cacheLife(profile: "weeks"): void;
/**
* Cache this `"use cache"` for a timespan defined by the `"max"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 2592000 seconds (30 days)
* expire: never
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 30 days, start revalidating new values in the background.
* It lives for the maximum age of the server cache. If this entry has no traffic for a while, it may serve an old value the next request.
*/
export function unstable_cacheLife(profile: "max"): void;
/**
* Cache this `"use cache"` using a custom timespan.
* ```
* stale: ... // seconds
* revalidate: ... // seconds
* expire: ... // seconds
* ```
*
* This is similar to Cache-Control: max-age=`stale`,s-max-age=`revalidate`,stale-while-revalidate=`expire-revalidate`
*
* If a value is left out, the lowest of other cacheLife() calls or the default, is used instead.
*/
export function unstable_cacheLife(profile: {
/**
* This cache may be stale on clients for ... seconds before checking with the server.
*/
stale?: number;
/**
* If the server receives a new request after ... seconds, start revalidating new values in the background.
*/
revalidate?: number;
/**
* If this entry has no traffic for ... seconds it will expire. The next request will recompute it.
*/
expire?: number;
}): void;
export { cacheTag as unstable_cacheTag } from "next/dist/server/use-cache/cache-tag";
}

1
types/package.json Normal file
View File

@@ -0,0 +1 @@
{"type": "module"}

7514
yarn.lock

File diff suppressed because it is too large Load Diff