2 Commits

Author SHA1 Message Date
SrJuggernaut df81e591a7 fix: rework branch naming updating to next 16
Squashed commit of the following:

commit 13fb596232
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Apr 2 12:59:26 2026 -0600

    chore(deps): upgrade sharp to v0.34.5, add @types/bun, remove @types/node

commit 17be2f09dd
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Apr 2 12:49:47 2026 -0600

    chore: add lint-staged with husky pre-commit hook for automated linting

commit b4a28fb35e
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Apr 2 12:49:29 2026 -0600

    chore: update husky hooks for latest version and use bun

commit 796c952890
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Apr 2 12:00:41 2026 -0600

    build: add Dockerfile and .dockerignore for Next.js containerization

commit 166ac4350f
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Apr 2 11:59:44 2026 -0600

    chore: moves next config to typescript

    Also adds DOCKER_BUILD env variable in prepare for moving out from github workflows

commit ea498588e0
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Apr 1 14:17:22 2026 -0600

    chore(deps): bump date-fns from 3.3.1 to 4.1.0

commit 3c66b5bad1
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Apr 1 14:16:31 2026 -0600

    chore: update @pandacss/dev to v1.9.1

commit 221034133d
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Apr 1 14:14:35 2026 -0600

    chore: update husky and commitlint

commit 4be0de62fc
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Apr 1 14:07:57 2026 -0600

    chore: update FontAwesome dependencies to v7.2.0

commit b595cba4b0
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Apr 1 14:01:16 2026 -0600

    chore: update TypeScript module config to esnext and bundler

commit b8454f750d
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Apr 1 13:51:25 2026 -0600

    feat: add suspense loading state to recover password page

commit d0f4a88661
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Apr 1 13:48:15 2026 -0600

    refactor: enhance TypeScript typing and import consistency

commit 57a6032a24
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Apr 1 13:46:02 2026 -0600

    chore: upgrade next and deps

commit f8018048bc
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Jul 31 18:44:51 2024 -0600

    fix: pm2 run with bun

commit ec196b2850
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Jul 31 18:35:29 2024 -0600

    fix: deploy

commit 8802b0fd68
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Tue Jul 30 18:23:15 2024 -0600

    feat: nextjs 14 (#20)

    * feat: eslint update

    * feat: start over and layout

    * feat: nextjs13 boilerplate

    * feat: static homepage

    * feat: static pages

    * feat: static unirse

    * chore: remove old mui types

    * chore: moving from yarn to bun

    * chore: update dependencies

    * feat: static equipo unirse

    * feat: move appwrite to entgamers-database package

    * feat: improve ui components

    * feat: update dependencies

    * feat: static login & register pages

    * fix: remove unused logs

    * feat: state redux toolkit & feedback slice

    * fix: equipo div inside p

    * feat: session

    * feat: metadataBase

    * feat: basic apply form

    * feat: http verbs

    * feat: recover password flow

    * chore: updated dependencies

    * fix: fix image config

    * fix: api team-applications route

    * fix: remove not longer used fonts

    * feat: session with current user

    * fix: login form recuperar contraseña

    * feat: equipo pages now uses data from database package

    * feat: useManageErrors hook

    * feat: updated cuenta page

    * chore: updated old formik forms to use hooks

    * feat: updated dependencies &package name

    * fix: session related bugs

    * fix: missing helper texts

    * feat: static applications dashboard

    * chore: update dependencies

    * refactor: team applications

    * fix: session api update

commit 14b52a7800
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Sep 14 21:15:53 2023 -0600

    fix: remove bundle analyzer

commit f11ae1c4f3
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Sep 14 21:08:33 2023 -0600

    revert: revert to yarn to deploy

commit f5a9a88f84
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Sep 14 14:52:25 2023 -0600

    fix: pm2 script

commit f59a7cc091
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Sep 14 14:49:04 2023 -0600

    fix: interpreter to use pm2 2

commit 3a831acaee
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Sep 14 14:39:51 2023 -0600

    fix: interpreter to use pm2

commit b514c6bc6f
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Sep 14 14:21:14 2023 -0600

    fix: bun doesnt run on pm2

commit ce87aa5ee3
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Sep 14 14:14:12 2023 -0600

    feat: moving deployments to bun

commit b26d5d8eba
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Thu Sep 14 13:01:22 2023 -0600

    chore: updated dependencies

commit 390f8bc858
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Tue Jul 11 15:46:34 2023 -0600

    feat: riot review

commit d926d3a5ef
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Jan 4 15:47:45 2023 -0600

    fix: github action name, wrong triggers

commit fdefa84ec7
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Jan 4 15:46:14 2023 -0600

    fix: github action naming

commit 9db5e5cebf
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Wed Jan 4 15:44:18 2023 -0600

    feat: setup github action to run on new server

commit 35105441cb
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Sun Oct 9 13:56:41 2022 -0500

    docs: deploy env vars

commit 7986456b9c
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Sun Oct 9 13:20:42 2022 -0500

    fix: production git ref

commit f9aea7eea4
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Sat Oct 1 13:34:31 2022 -0500

    fix: glass text contrast

commit a68098e7e2
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Sat Oct 1 13:33:29 2022 -0500

    ci: use yarn instead npm

commit 864ff91255
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Fri Sep 30 19:32:40 2022 -0500

    fix: server use isomorphic-fetch

commit c69a166c1f
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Fri Sep 30 18:23:38 2022 -0500

    revert: deploy as sudo

    Refs: 624b225

commit 731c9b8962
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Fri Sep 30 17:50:41 2022 -0500

    fix: pass only required env to deploy

commit 624b2251c4
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Fri Sep 30 13:57:27 2022 -0500

    feat: post deploy as sudo

commit b73cc51e08
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Fri Sep 30 13:41:56 2022 -0500

    fix: pass environment to actions

commit a35e99f8ff
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Thu Sep 29 21:54:58 2022 -0500

    ci: deploy using pm2

    * ci: pm2 configuration file

    * ci: github action deploy preview

    * ci: github action deploy production

    * ci: env variables now pass to pm2

commit 4f8c4f6492
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Wed Sep 28 18:09:10 2022 -0500

    docs: initial documentation

commit c3dae929c6
Author: Jugger <jugger@srjuggernaut.dev>
Date:   Mon Sep 26 12:01:26 2022 -0500

    feat: static site

    * feat: mui support & basic theming

    * feat: entgamers favicon

    * feat: public images until dynamic content can be used

    * feat: entgamers & gaming assets

    * feat: eslint extra rules

    * feat: mui theme modifications

    * feat: fontawesome, gsap, bundle analyzer

    * feat: common interfaces

    * feat: basic layout

    * chore: upadted dependencies

    * chore: updated dependencies

    * feat: updated link styles

    * feat: layout now have better interfaces

    * feat: basic seo component

    * feat: static website

    * feat: env variable rules in .gitignore

    * feat: added lint to pre-commit

commit 8573d61066
Author: SrJuggernaut <jugger@srjuggernaut.dev>
Date:   Sat Aug 13 11:29:32 2022 -0500

    Initial commit from Create Next App
2026-04-02 14:47:17 -06:00
SrJuggernaut 025a4daa06 fix: clean initial commit 2026-04-02 14:41:38 -06:00
64 changed files with 791 additions and 606 deletions
+134
View File
@@ -0,0 +1,134 @@
############################################################
# Production-ready .dockerignore for a Next.js app
# Keeps Docker builds fast, lean, and free of development files.
############################################################
# Dependencies (installed inside Docker, never copied)
node_modules/
.pnpm-store/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Next.js build outputs (always generated during `next build`)
.next/
out/
dist/
build/
.vercel/
# Tests and testing output (not needed in production images)
coverage/
.nyc_output/
__tests__/
__mocks__/
jest/
cypress/
cypress/screenshots/
cypress/videos/
playwright-report/
test-results/
.vitest/
vitest.config.*
jest.config.*
cypress.config.*
playwright.config.*
*.test.*
*.spec.*
# Local development and editor files
.git/
.gitignore
.gitattributes
.vscode/
.idea/
*.swp
*.swo
*~
*.log
# Environment variables (only commit template files)
.env
.env*.local
.env.development
.env.test
.env.production.local
# Docker configuration files (not needed inside build context)
Dockerfile*
.dockerignore
compose.yaml
compose.yml
docker-compose*.yaml
docker-compose*.yml
# Documentation
*.md
docs/
# CI/CD configuration files
.github/
.gitlab-ci.yml
.travis.yml
.circleci/
Jenkinsfile
# Cache directories and temporary data
.cache/
.parcel-cache/
.eslintcache
.stylelintcache
.swc/
.turbo/
.tmp/
.temp/
# TypeScript build metadata
*.tsbuildinfo
# Sensitive or unnecessary configuration files
*.pem
.editorconfig
.prettierrc*
prettier.config.*
.eslintrc*
eslint.config.*
.stylelintrc*
stylelint.config.*
.babelrc*
*.iml
*.ipr
*.iws
# OS-specific junk
.DS_Store
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
Desktop.ini
# AI/ML tool metadata and configs
.cursor/
.cursorrules
.copilot/
.copilotignore
.github/copilot/
.gemini/
.anthropic/
.kiro
.claude
AGENTS.md
.agents/
# AI-generated temp files
*.aider*
*.copilot*
*.chatgpt*
*.claude*
*.gemini*
*.openai*
*.anthropic*
-1
View File
@@ -1 +0,0 @@
src/styled-system
-55
View File
@@ -1,55 +0,0 @@
{
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"next/core-web-vitals",
"standard-with-typescript",
"plugin:react/recommended"
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
"react/react-in-jsx-scope": "off",
"react/jsx-uses-react": "off",
"indent": "off",
"@typescript-eslint/indent": [
"error",
2,
{
"SwitchCase": 1
}
],
"react/jsx-indent": [
"error",
2
],
"no-use-before-define": "off",
"@typescript-eslint/no-use-before-define": [
"error"
],
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_"
}
],
"react-hooks/exhaustive-deps": "off",
"react/no-unknown-property": [
"error",
{
"ignore": [
"css"
]
}
]
}
}
-38
View File
@@ -1,38 +0,0 @@
name: CI deploy preview
on:
push:
branches: ["preview"]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
environment: preview
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: kielabokkie/ssh-key-and-known-hosts-action@v1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-host: ${{ secrets.DEPLOY_HOST }}
- name: Install pm2
run: bun install pm2 -g
shell: bash
- name: Deploy using pm2
run: pm2 deploy ecosystem.config.js preview
env:
# Deploy environment variables
APP_NAME: ${{ secrets.APP_NAME }}
PORT: ${{ secrets.PORT }}
SSH_USERNAME: ${{ secrets.SSH_USERNAME }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
# Appwrite variables
NEXT_PUBLIC_APPWRITE_ENDPOINT: ${{ secrets.NEXT_PUBLIC_APPWRITE_ENDPOINT }}
NEXT_PUBLIC_APPWRITE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_APPWRITE_PROJECT_ID }}
APPWRITE_API_KEY: ${{ secrets.APPWRITE_API_KEY }}
# Website variables
SITE_NAME: ${{ secrets.SITE_NAME }}
NEXT_PUBLIC_SITE_URL: ${{ secrets.NEXT_PUBLIC_SITE_URL }}
IMAGE_DOMAINS: ${{ secrets.IMAGE_DOMAINS }}
shell: bash
-38
View File
@@ -1,38 +0,0 @@
name: CI deploy production
on:
push:
branches: ["production"]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: kielabokkie/ssh-key-and-known-hosts-action@v1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-host: ${{ secrets.DEPLOY_HOST }}
- name: Install pm2
run: bun install pm2 -g
shell: bash
- name: Deploy using pm2
run: pm2 deploy ecosystem.config.js production
env:
# Deploy environment variables
APP_NAME: ${{ secrets.APP_NAME }}
PORT: ${{ secrets.PORT }}
SSH_USERNAME: ${{ secrets.SSH_USERNAME }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
# Appwrite variables
NEXT_PUBLIC_APPWRITE_ENDPOINT: ${{ secrets.NEXT_PUBLIC_APPWRITE_ENDPOINT }}
NEXT_PUBLIC_APPWRITE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_APPWRITE_PROJECT_ID }}
APPWRITE_API_KEY: ${{ secrets.APPWRITE_API_KEY }}
# Website variables
SITE_NAME: ${{ secrets.SITE_NAME }}
NEXT_PUBLIC_SITE_URL: ${{ secrets.NEXT_PUBLIC_SITE_URL }}
IMAGE_DOMAINS: ${{ secrets.IMAGE_DOMAINS }}
shell: bash
-36
View File
@@ -1,36 +0,0 @@
name: CI setup preview
on:
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
environment: preview
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1
- uses: kielabokkie/ssh-key-and-known-hosts-action@v1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-host: ${{ secrets.DEPLOY_HOST }}
- name: Install pm2
run: bun install pm2 -g
shell: bash
- name: Deploy using pm2
run: pm2 deploy ecosystem.config.js preview setup
env:
# Deploy environment variables
APP_NAME: ${{ secrets.APP_NAME }}
PORT: ${{ secrets.PORT }}
SSH_USERNAME: ${{ secrets.SSH_USERNAME }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
# Appwrite variables
NEXT_PUBLIC_APPWRITE_ENDPOINT: ${{ secrets.NEXT_PUBLIC_APPWRITE_ENDPOINT }}
NEXT_PUBLIC_APPWRITE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_APPWRITE_PROJECT_ID }}
APPWRITE_API_KEY: ${{ secrets.APPWRITE_API_KEY }}
# Website variables
SITE_NAME: ${{ secrets.SITE_NAME }}
NEXT_PUBLIC_SITE_URL: ${{ secrets.NEXT_PUBLIC_SITE_URL }}
IMAGE_DOMAINS: ${{ secrets.IMAGE_DOMAINS }}
shell: bash
-36
View File
@@ -1,36 +0,0 @@
name: CI setup production
on:
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1
- uses: kielabokkie/ssh-key-and-known-hosts-action@v1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-host: ${{ secrets.DEPLOY_HOST }}
- name: Install pm2
run: bun install pm2 -g
shell: bash
- name: Deploy using pm2
run: pm2 deploy ecosystem.config.js production setup
env:
# Deploy environment variables
APP_NAME: ${{ secrets.APP_NAME }}
PORT: ${{ secrets.PORT }}
SSH_USERNAME: ${{ secrets.SSH_USERNAME }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
# Appwrite variables
NEXT_PUBLIC_APPWRITE_ENDPOINT: ${{ secrets.NEXT_PUBLIC_APPWRITE_ENDPOINT }}
NEXT_PUBLIC_APPWRITE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_APPWRITE_PROJECT_ID }}
APPWRITE_API_KEY: ${{ secrets.APPWRITE_API_KEY }}
# Website variables
SITE_NAME: ${{ secrets.SITE_NAME }}
NEXT_PUBLIC_SITE_URL: ${{ secrets.NEXT_PUBLIC_SITE_URL }}
IMAGE_DOMAINS: ${{ secrets.IMAGE_DOMAINS }}
shell: bash
+1 -4
View File
@@ -1,4 +1 @@
#!/bin/sh bunx --no -- commitlint --edit "${1}"
. "$(dirname "$0")/_/husky.sh"
npx --no -- commitlint --edit "${1}"
+9
View File
@@ -0,0 +1,9 @@
// Skip Husky install in production and CI
if (process.env.NODE_ENV === 'production' || process.env.CI === 'true') {
process.exit(0)
}
const husky = (await import('husky')).default
console.log(husky())
+1
View File
@@ -0,0 +1 @@
bunx lint-staged --config lint-staged.config.ts
-4
View File
@@ -1,4 +0,0 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint
+52
View File
@@ -0,0 +1,52 @@
FROM oven/bun:1.3-alpine AS builder
WORKDIR /app
COPY . .
ARG NEXT_PUBLIC_APPWRITE_ENDPOINT=""
ARG NEXT_PUBLIC_APPWRITE_PROJECT_ID=""
ARG SITE_NAME="EntGamers"
ARG NEXT_PUBLIC_SITE_URL="https://entgamers.pro"
ARG IMAGE_DOMAINS="https://api.entgamers.pro"
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV CI=1
ENV HUSKY=0
ENV DOCKER_BUILD="true"
RUN bun install --frozen-lockfile
RUN --mount=type=secret,id=APPWRITE_API_KEY,env=APPWRITE_API_KEY \
NEXT_PUBLIC_APPWRITE_ENDPOINT=${NEXT_PUBLIC_APPWRITE_ENDPOINT} \
NEXT_PUBLIC_APPWRITE_PROJECT_ID=${NEXT_PUBLIC_APPWRITE_PROJECT_ID} \
SITE_NAME=${SITE_NAME} \
NEXT_PUBLIC_SITE_URL=${NEXT_PUBLIC_SITE_URL} \
IMAGE_DOMAINS=${IMAGE_DOMAINS} \
bun run build
FROM oven/bun:1.3-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
ENV NEXT_TELEMETRY_DISABLED=1
COPY --from=builder --chown=bun:bun /app/public ./public
COPY --from=builder --chown=bun:bun /app/.next/standalone ./
COPY --from=builder --chown=bun:bun /app/.next/static ./.next/static
USER bun
EXPOSE 3000
CMD ["bun", "server.js"]
BIN
View File
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
module.exports = { extends: ['@commitlint/config-conventional'] }
+5
View File
@@ -0,0 +1,5 @@
import type { UserConfig } from '@commitlint/types'
const Configuration: UserConfig = { extends: ['@commitlint/config-conventional'] }
export default Configuration
+13 -13
View File
@@ -2,7 +2,7 @@ module.exports = {
apps: [ apps: [
{ {
name: process.env.APP_NAME || 'entgamers-website', name: process.env.APP_NAME || 'entgamers-website',
script: 'yarn', script: 'bun',
args: 'run start', args: 'run start',
env: { env: {
NODE_ENV: 'production', NODE_ENV: 'production',
@@ -19,13 +19,13 @@ module.exports = {
deploy: { deploy: {
production: { production: {
user: process.env.SSH_USERNAME, 'user': process.env.SSH_USERNAME,
host: process.env.DEPLOY_HOST, 'host': process.env.DEPLOY_HOST,
ref: 'origin/production', 'ref': 'origin/production',
repo: 'https://github.com/SrJuggernaut/entgamers_pro', 'repo': 'https://github.com/SrJuggernaut/entgamers_pro',
path: process.env.DEPLOY_PATH, 'path': process.env.DEPLOY_PATH,
'post-deploy': 'pm2 --silent startOrRestart ecosystem.config.js', 'post-deploy': 'pm2 --silent startOrRestart ecosystem.config.js',
env: { 'env': {
PORT: process.env.PORT, PORT: process.env.PORT,
NEXT_PUBLIC_APPWRITE_ENDPOINT: process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT, NEXT_PUBLIC_APPWRITE_ENDPOINT: process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT,
NEXT_PUBLIC_APPWRITE_PROJECT_ID: process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID, NEXT_PUBLIC_APPWRITE_PROJECT_ID: process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID,
@@ -37,13 +37,13 @@ module.exports = {
}, },
preview: { preview: {
user: process.env.SSH_USERNAME, 'user': process.env.SSH_USERNAME,
host: process.env.DEPLOY_HOST, 'host': process.env.DEPLOY_HOST,
ref: 'origin/preview', 'ref': 'origin/preview',
repo: 'https://github.com/SrJuggernaut/entgamers_pro', 'repo': 'https://github.com/SrJuggernaut/entgamers_pro',
path: process.env.DEPLOY_PATH, 'path': process.env.DEPLOY_PATH,
'post-deploy': 'pm2 --silent startOrRestart ecosystem.config.js', 'post-deploy': 'pm2 --silent startOrRestart ecosystem.config.js',
env: { 'env': {
PORT: process.env.PORT, PORT: process.env.PORT,
NEXT_PUBLIC_APPWRITE_ENDPOINT: process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT, NEXT_PUBLIC_APPWRITE_ENDPOINT: process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT,
NEXT_PUBLIC_APPWRITE_PROJECT_ID: process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID, NEXT_PUBLIC_APPWRITE_PROJECT_ID: process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID,
+98
View File
@@ -0,0 +1,98 @@
import eslintJs from '@eslint/js'
import nextPlugin from '@next/eslint-plugin-next'
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'
import reactPlugin from 'eslint-plugin-react'
import reactHooksPlugin from 'eslint-plugin-react-hooks'
import { defineConfig, globalIgnores } from 'eslint/config'
import globals from 'globals'
import tseslint from 'typescript-eslint'
import stylisticPlugin from '@stylistic/eslint-plugin'
export default defineConfig([
globalIgnores([
'node_modules/',
'public/', 'src/styled-system/**/*', '.next/**',
'out/**',
'build/**',
'next-env.d.ts']),
{
name: 'entgamers_pro/globals',
languageOptions: {
globals: {
...globals.browser,
...globals.node
}
}
},
{
name: 'project/eslint-js',
files: ['**/*.{js,mjs,ts,tsx}'],
...eslintJs.configs.recommended
},
{
name: 'project/typescript',
files: ['**/*.{ts,tsx}'],
extends: [
...tseslint.configs.recommended
],
rules: {
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }]
}
},
{
name: 'project/react-next',
files: ['**/*.{jsx,tsx}'],
plugins: {
'react': reactPlugin,
'react-hooks': reactHooksPlugin,
'jsx-a11y': jsxA11yPlugin,
'@next/next': nextPlugin
},
rules: {
...reactPlugin.configs.recommended.rules,
...reactPlugin.configs['jsx-runtime'].rules,
...reactHooksPlugin.configs['recommended-latest'].rules,
...jsxA11yPlugin.configs.strict.rules,
...nextPlugin.configs.recommended.rules,
...nextPlugin.configs['core-web-vitals'].rules,
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
'react/no-unknown-property': 'off',
'react/jsx-no-target-blank': 'off',
'jsx-a11y/alt-text': ['warn', { elements: ['img'], img: ['Image'] }],
'jsx-a11y/media-has-caption': 'warn'
},
settings: {
react: {
version: '19'
}
}
},
{
name: 'project/stylistic',
files: ['**/*.{js,mjs,ts,tsx}'],
plugins: {
'@stylistic': stylisticPlugin
},
rules: {
// Remove legacy formatting rules from ESLint core
...stylisticPlugin.configs['disable-legacy'].rules,
// Add recommended stylistic rules
...stylisticPlugin.configs.recommended.rules,
'@stylistic/indent': ['warn', 2],
'@stylistic/quotes': ['warn', 'single', {
avoidEscape: true,
allowTemplateLiterals: 'always'
}],
'@stylistic/semi': ['warn', 'never'],
'@stylistic/comma-dangle': ['warn', 'never'],
'@stylistic/arrow-parens': ['warn', 'always', {
requireForBlockBody: true
}],
'@stylistic/brace-style': ['warn', '1tbs', {
allowSingleLine: true
}]
}
}
])
+7
View File
@@ -0,0 +1,7 @@
import type { Configuration } from 'lint-staged'
const config: Configuration = {
'*.{js,jsx,ts,tsx}': 'eslint --fix'
}
export default config
+2 -1
View File
@@ -1,5 +1,6 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";
// NOTE: This file should not be edited // 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.
-15
View File
@@ -1,15 +0,0 @@
const imageDomains = (process.env.IMAGE_DOMAINS ?? '').split(',').map(domain => {
const getDataRegex = /(?<protocol>[\w]+)?:\/\/(?<hostname>[\w.-]+)?((?<=[\d]{0,4}):(?<port>[\d]{0,4}))?\/?(?<pathname>.*)?$/
const groups = getDataRegex.exec(domain).groups ?? {}
return groups
})
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
images: {
remotePatterns: imageDomains
}
}
module.exports = nextConfig
+17
View File
@@ -0,0 +1,17 @@
import type { NextConfig } from 'next'
const imageDomains = (process.env.IMAGE_DOMAINS ?? '').split(',').map((domain) => {
return new URL(domain)
})
const IS_DOCKER_BUILD = process.env.DOCKER_BUILD === 'true'
const nextConfig: NextConfig = {
output: IS_DOCKER_BUILD ? 'standalone' : undefined,
reactStrictMode: true,
images: {
remotePatterns: imageDomains
}
}
module.exports = nextConfig
+28 -33
View File
@@ -5,56 +5,51 @@
"scripts": { "scripts": {
"develop": "next dev", "develop": "next dev",
"build": "next build", "build": "next build",
"prestart": "bun install && next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "eslint .",
"prepare": "panda codegen && husky install" "prepare": "panda codegen && bun ./.husky/install.mts"
}, },
"dependencies": { "dependencies": {
"@fontsource/open-sans": "^5.0.20", "@fontsource/open-sans": "^5.0.20",
"@fontsource/permanent-marker": "^5.0.8", "@fontsource/permanent-marker": "^5.0.8",
"@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/fontawesome-svg-core": "^7.2.0",
"@fortawesome/free-brands-svg-icons": "^6.5.1", "@fortawesome/free-brands-svg-icons": "^7.2.0",
"@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^7.2.0",
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^3.3.0",
"@reduxjs/toolkit": "^2.0.1", "@reduxjs/toolkit": "^2.0.1",
"@stylistic/eslint-plugin": "^5.10.0",
"@tanstack/match-sorter-utils": "^8.11.8", "@tanstack/match-sorter-utils": "^8.11.8",
"@tanstack/react-table": "^8.19.3", "@tanstack/react-table": "^8.19.3",
"appwrite": "^13.0.1", "appwrite": "^13.0.1",
"date-fns": "^3.3.1", "date-fns": "^4.1.0",
"entgamers-database": "0.0.26", "entgamers-database": "0.0.26",
"entgamers-panda-preset": "0.1.5", "entgamers-panda-preset": "0.1.5",
"formik": "^2.4.5", "formik": "^2.4.5",
"framer-motion": "^10.17.6", "framer-motion": "^12.38.0",
"isomorphic-fetch": "^3.0.0", "isomorphic-fetch": "^3.0.0",
"next": "^14.0.4", "lint-staged": "^16.4.0",
"next": "16.2.2",
"node-appwrite": "^11.1.0", "node-appwrite": "^11.1.0",
"react": "18.2.0", "react": "19.2.4",
"react-dom": "18.2.0", "react-dom": "19.2.4",
"react-redux": "^9.0.4", "react-redux": "^9.0.4",
"sharp": "^0.33.1", "sharp": "^0.34.5",
"yup": "^1.3.3" "yup": "^1.3.3"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^18.4.4", "@commitlint/cli": "^20.5.0",
"@commitlint/config-conventional": "^18.4.4", "@commitlint/config-conventional": "^20.5.0",
"@pandacss/dev": "^0.23.0", "@eslint/js": "^10.0.1",
"@pandacss/dev": "^1.9.1",
"@types/bun": "^1.3.11",
"@types/isomorphic-fetch": "^0.0.39", "@types/isomorphic-fetch": "^0.0.39",
"@types/node": "^20.10.6", "@types/react": "19.2.14",
"@types/react": "^18.2.46", "@types/react-dom": "19.2.3",
"@types/react-dom": "^18.2.18", "eslint": "^10.1.0",
"@typescript-eslint/eslint-plugin": "^6.4.0", "eslint-config-next": "16.2.2",
"@typescript-eslint/parser": "^5.38.0", "eslint-plugin-react": "^7.37.5",
"eslint": "^8.0.1", "husky": "^9.1.7",
"eslint-config-next": "latest", "typescript": "*",
"eslint-config-standard": "^17.0.0", "typescript-eslint": "^8.58.0"
"eslint-config-standard-with-typescript": "latest",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "latest",
"husky": "^8.0.3",
"typescript": "*"
} }
} }
+1 -1
View File
@@ -22,7 +22,7 @@ const Clanes: FC = () => {
<div <div
className={card({ variant: 'glass' }).content} className={card({ variant: 'glass' }).content}
> >
<Typography variant="h2" align='center'>Clanes</Typography> <Typography variant="h2" align="center">Clanes</Typography>
<div <div
className={css({ className={css({
display: 'grid', display: 'grid',
+3 -3
View File
@@ -9,11 +9,11 @@ import { alert } from '@/styled-system/recipes/alert'
import { faTimes } from '@fortawesome/free-solid-svg-icons' import { faTimes } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { AnimatePresence, motion } from 'framer-motion' import { AnimatePresence, motion } from 'framer-motion'
import { type FC } from 'react' import type { FC } from 'react'
import { createPortal } from 'react-dom' import { createPortal } from 'react-dom'
const FeedbackConsumer: FC = () => { const FeedbackConsumer: FC = () => {
const { alerts } = useAppSelector(state => state.feedback) const { alerts } = useAppSelector((state) => state.feedback)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
return ( return (
<> <>
@@ -55,7 +55,7 @@ const FeedbackConsumer: FC = () => {
className={alert().closeButton} className={alert().closeButton}
onClick={() => dispatch(removeAlert(currentAlert.id))} onClick={() => dispatch(removeAlert(currentAlert.id))}
> >
<FontAwesomeIcon icon={faTimes} fixedWidth size='sm'/> <FontAwesomeIcon icon={faTimes} size="sm" />
</IconButton> </IconButton>
<Typography variant="h3" component="div"> <Typography variant="h3" component="div">
{currentAlert.title} {currentAlert.title}
+9 -9
View File
@@ -111,20 +111,20 @@ const Hero: FC = () => {
color: 'primary', color: 'primary',
size: 'large' size: 'large'
}), css({ }), css({
position: 'absolute', 'position': 'absolute',
bottom: '45px', 'bottom': '45px',
right: '50%', 'right': '50%',
animationName: 'bounce', 'animationName': 'bounce',
animationDuration: '1s', 'animationDuration': '1s',
animationIterationCount: 'infinite', 'animationIterationCount': 'infinite',
transform: 'translateX(50%)', 'transform': 'translateX(50%)',
zIndex: 1, 'zIndex': 1,
'&:hover': { '&:hover': {
animationPlayState: 'paused' animationPlayState: 'paused'
} }
}))} }))}
> >
<FontAwesomeIcon icon={faArrowDown} size='lg' fixedWidth /> <FontAwesomeIcon icon={faArrowDown} size="lg" />
</a> </a>
</section> </section>
) )
+6 -10
View File
@@ -5,7 +5,7 @@ import { setClanes, setCurrentUser, setSession, setStatus } from '@/state/sessio
import { AppwriteException } from 'appwrite' import { AppwriteException } from 'appwrite'
import { getClanes } from 'entgamers-database/frontend/clanes' import { getClanes } from 'entgamers-database/frontend/clanes'
import { getCurrentUser, getSession } from 'entgamers-database/frontend/session' import { getCurrentUser, getSession } from 'entgamers-database/frontend/session'
import { useCallback, useEffect, type FC } from 'react' import { type FC, useCallback, useEffect } from 'react'
const SessionConsumer: FC = () => { const SessionConsumer: FC = () => {
const { status, session, user, clanes } = useAppSelector((state) => state.session) const { status, session, user, clanes } = useAppSelector((state) => state.session)
@@ -13,7 +13,6 @@ const SessionConsumer: FC = () => {
const ensureSession = useCallback(async () => { const ensureSession = useCallback(async () => {
try { try {
if (status !== 'initializing' || session !== undefined) return
dispatch(setStatus('loading')) dispatch(setStatus('loading'))
const currentSession = await getSession('current') const currentSession = await getSession('current')
const currentUser = await getCurrentUser() const currentUser = await getCurrentUser()
@@ -26,16 +25,17 @@ const SessionConsumer: FC = () => {
} finally { } finally {
dispatch(setStatus('idle')) dispatch(setStatus('idle'))
} }
}, []) }, [dispatch])
useEffect(() => { useEffect(() => {
if (status !== 'initializing' || session !== undefined) return
ensureSession() ensureSession()
.catch((error) => { .catch((error) => {
if (error instanceof AppwriteException) { if (error instanceof AppwriteException) {
console.error(error) console.error(error)
} }
}) })
}, []) }, [status, session, ensureSession])
useEffect(() => { useEffect(() => {
if (user !== undefined && clanes === undefined) { if (user !== undefined && clanes === undefined) {
@@ -51,11 +51,7 @@ const SessionConsumer: FC = () => {
} else if (user === undefined && clanes !== undefined) { } else if (user === undefined && clanes !== undefined) {
dispatch(setClanes()) dispatch(setClanes())
} }
}, [user]) }, [user, clanes, dispatch])
return null
return (
<>
</>
)
} }
export default SessionConsumer export default SessionConsumer
+1 -1
View File
@@ -52,7 +52,7 @@ const Social: FC = () => {
<div <div
className={card({ variant: 'glass' }).content} className={card({ variant: 'glass' }).content}
> >
<Typography variant="h2" align='center'>Redes Sociales</Typography> <Typography variant="h2" align="center">Redes Sociales</Typography>
<Typography variant="body1"> <Typography variant="body1">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptate deleniti dolore quas sed nemo sit, officia in rem nesciunt quisquam possimus ab! Labore sed reprehenderit quae, hic earum tempora placeat cumque id eos itaque perferendis nulla officia fuga porro, quis, unde facere accusamus repudiandae non? Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptate deleniti dolore quas sed nemo sit, officia in rem nesciunt quisquam possimus ab! Labore sed reprehenderit quae, hic earum tempora placeat cumque id eos itaque perferendis nulla officia fuga porro, quis, unde facere accusamus repudiandae non?
</Typography> </Typography>
+1 -1
View File
@@ -84,7 +84,7 @@ const Team: FC = () => {
className={iconButton()} className={iconButton()}
href={socialNetwork.url} href={socialNetwork.url}
> >
<FontAwesomeIcon icon={socialNetwork.icon} fixedWidth /> <FontAwesomeIcon icon={socialNetwork.icon} />
</a> </a>
))} ))}
</div> </div>
+55 -14
View File
@@ -3,7 +3,7 @@ import { css } from '@/styled-system/css'
import { Container } from '@/styled-system/jsx' import { Container } from '@/styled-system/jsx'
import { faChevronRight } from '@fortawesome/free-solid-svg-icons' import { faChevronRight } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { type FC } from 'react' import type { FC } from 'react'
const ClanesPage: FC = () => { const ClanesPage: FC = () => {
return ( return (
@@ -21,37 +21,78 @@ const ClanesPage: FC = () => {
<Typography variant="h2">Beneficios de los clanes</Typography> <Typography variant="h2">Beneficios de los clanes</Typography>
<Typography variant="body1">La intención de EntGamers es brindar beneficios a los clanes que les permitan operar en un ambiente de comunicación y colaboración.</Typography> <Typography variant="body1">La intención de EntGamers es brindar beneficios a los clanes que les permitan operar en un ambiente de comunicación y colaboración.</Typography>
<ul className="fa-ul"> <ul className="fa-ul">
<li><FontAwesomeIcon icon={faChevronRight} listItem /> Espacio en el servidor de Discord.</li> <li>
<li><FontAwesomeIcon icon={faChevronRight} listItem /> Apoyo de la administración con proyectos y eventos.</li> <span className="fa-li"><FontAwesomeIcon icon={faChevronRight} /></span>
<li><FontAwesomeIcon icon={faChevronRight} listItem /> Apoyo del equipo de moderación.</li> {' '}
Espacio en el servidor de Discord.
</li>
<li>
<span className="fa-li"><FontAwesomeIcon icon={faChevronRight} /></span>
{' '}
Apoyo de la administración con proyectos y eventos.
</li>
<li>
<span className="fa-li">
<FontAwesomeIcon icon={faChevronRight} />
</span>
{' '}
Apoyo del equipo de moderación.
</li>
</ul> </ul>
</div> </div>
<div> <div>
<Typography variant="h2">Requisitos para formar un clan</Typography> <Typography variant="h2">Requisitos para formar un clan</Typography>
<Typography variant="body1">Todos los clanes deben cumplir con los siguientes requisitos:</Typography> <Typography variant="body1">Todos los clanes deben cumplir con los siguientes requisitos:</Typography>
<ul className="fa-ul"> <ul className="fa-ul">
<li><FontAwesomeIcon icon={faChevronRight} listItem /> Tener un encargado.</li> <li>
<li><FontAwesomeIcon icon={faChevronRight} listItem /> Fomentar el compañerismo y la comunidad.</li> <span className="fa-li">
<li><FontAwesomeIcon icon={faChevronRight} listItem /> Aportar contenido de forma periódica para la comunidad.</li> <FontAwesomeIcon icon={faChevronRight} />
<li><FontAwesomeIcon icon={faChevronRight} listItem /> Realizar al menos una actividad mensual con los integrantes.</li> </span>
{' '}
Tener un encargado.
</li>
<li>
<span className="fa-li">
<FontAwesomeIcon icon={faChevronRight} />
</span>
{' '}
Fomentar el compañerismo y la comunidad.
</li>
<li>
<span className="fa-li">
<FontAwesomeIcon icon={faChevronRight} />
</span>
{' '}
Aportar contenido de forma periódica para la comunidad.
</li>
<li>
<span className="fa-li">
<FontAwesomeIcon icon={faChevronRight} />
</span>
{' '}
Realizar al menos una actividad mensual con los integrantes.
</li>
</ul> </ul>
</div> </div>
</div> </div>
<Typography variant="h2">Clanes activos</Typography> <Typography variant="h2">Clanes activos</Typography>
<div <div
className={css({ className={css({
backgroundColor: 'info', 'backgroundColor': 'info',
color: 'info.contrast', 'color': 'info.contrast',
borderRadius: 'medium', 'borderRadius': 'medium',
padding: 'medium', 'padding': 'medium',
marginBlock: 'medium', 'marginBlock': 'medium',
'& a': { '& a': {
color: 'info.contrast', color: 'info.contrast',
fontWeight: 'bold' fontWeight: 'bold'
} }
})} })}
> >
Esta sección está en construcción. Puedes ver los clanes activos en nuestro <a href="http://discord.gg/nqwzHJC">Servidor de Discord</a>. Esta sección está en construcción. Puedes ver los clanes activos en nuestro
{' '}
<a href="http://discord.gg/nqwzHJC">Servidor de Discord</a>
.
</div> </div>
</Container> </Container>
) )
+5 -3
View File
@@ -44,7 +44,7 @@ const CuentaTabs: FC = () => {
> >
<AnimatePresence <AnimatePresence
initial={false} initial={false}
mode='wait' mode="wait"
> >
{currentTab === 'login' && ( {currentTab === 'login' && (
<motion.div <motion.div
@@ -52,7 +52,8 @@ const CuentaTabs: FC = () => {
initial={{ opacity: 0, x: '-100%' }} initial={{ opacity: 0, x: '-100%' }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: '100%' }} exit={{ opacity: 0, x: '100%' }}
key="login"> key="login"
>
<UpdateEmail /> <UpdateEmail />
<UpdatePassword /> <UpdatePassword />
</motion.div> </motion.div>
@@ -63,7 +64,8 @@ const CuentaTabs: FC = () => {
initial={{ opacity: 0, x: '-100%' }} initial={{ opacity: 0, x: '-100%' }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: '100%' }} exit={{ opacity: 0, x: '100%' }}
key="perfil"> key="perfil"
>
<UpdateUserName /> <UpdateUserName />
<UpdateUserPreferences /> <UpdateUserPreferences />
</motion.div> </motion.div>
+2 -3
View File
@@ -68,7 +68,7 @@ const UpdateUserName: FC = () => {
}) })
.catch(console.error) .catch(console.error)
} }
}, [status, session, user]) }, [status, session, user, formik])
if (status !== 'idle' || session === undefined) { if (status !== 'idle' || session === undefined) {
// TODO: Replace with Skeleton // TODO: Replace with Skeleton
@@ -99,8 +99,7 @@ const UpdateUserName: FC = () => {
<Typography variant="caption" color="danger">{formik.errors.name}</Typography> <Typography variant="caption" color="danger">{formik.errors.name}</Typography>
)} )}
</FormGroup> </FormGroup>
<FormGroup <FormGroup>
>
<Button <Button
type="submit" type="submit"
disabled={formik.isSubmitting || !formik.isValid} disabled={formik.isSubmitting || !formik.isValid}
+6 -6
View File
@@ -55,7 +55,7 @@ const UpdateUserPreferences: FC = () => {
}) })
.catch(console.error) .catch(console.error)
} }
}, [status, session]) }, [status, user, formik, session])
if (status !== 'idle' || session === undefined) { if (status !== 'idle' || session === undefined) {
// TODO: Replace with Skeleton // TODO: Replace with Skeleton
@@ -65,7 +65,7 @@ const UpdateUserPreferences: FC = () => {
return ( return (
<> <>
<Typography <Typography
variant='h3' variant="h3"
> >
Preferencias Preferencias
</Typography> </Typography>
@@ -74,13 +74,13 @@ const UpdateUserPreferences: FC = () => {
> >
<FormGroup> <FormGroup>
<label <label
htmlFor='bio' htmlFor="bio"
> >
Biografia Biografia
</label> </label>
<TextArea <TextArea
id='bio' id="bio"
name='bio' name="bio"
value={formik.values.bio} value={formik.values.bio}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -96,7 +96,7 @@ const UpdateUserPreferences: FC = () => {
{/* TODO: Add Profile Picture and Social Links fields */} {/* TODO: Add Profile Picture and Social Links fields */}
<FormGroup> <FormGroup>
<Button <Button
type='submit' type="submit"
disabled={formik.isSubmitting || !formik.isValid} disabled={formik.isSubmitting || !formik.isValid}
fullWidth fullWidth
> >
+2 -2
View File
@@ -1,6 +1,6 @@
import Typography from '@/components/ui/Typography' import Typography from '@/components/ui/Typography'
import { Container } from '@/styled-system/jsx' import { Container } from '@/styled-system/jsx'
import { type FC } from 'react' import type { FC } from 'react'
import CuentaTabs from './CuentaTabs' import CuentaTabs from './CuentaTabs'
const CuentaPage: FC = () => { const CuentaPage: FC = () => {
@@ -10,7 +10,7 @@ const CuentaPage: FC = () => {
<Typography variant="body1"> <Typography variant="body1">
Desde aquí puedes administrar las preferencias y ajustes de tu cuenta. Desde aquí puedes administrar las preferencias y ajustes de tu cuenta.
</Typography> </Typography>
<CuentaTabs/> <CuentaTabs />
</Container> </Container>
) )
} }
@@ -40,7 +40,7 @@ const DashboardTabs: FC = () => {
> >
<AnimatePresence <AnimatePresence
initial={false} initial={false}
mode='wait' mode="wait"
> >
{currentTab === undefined && ( {currentTab === undefined && (
<motion.div <motion.div
@@ -17,7 +17,7 @@ const ApplicationsFilter: FC<ApplicationsFilterProps> = ({ column }) => {
<StatusSelector <StatusSelector
id={`${column.id}-status-filter`} id={`${column.id}-status-filter`}
value={columnFilterValue as TeamApplicationStatus} value={columnFilterValue as TeamApplicationStatus}
onChange={value => { column.setFilterValue(value) }} onChange={(value) => { column.setFilterValue(value) }}
allowEmpty allowEmpty
/> />
) )
@@ -26,7 +26,7 @@ const ApplicationsFilter: FC<ApplicationsFilterProps> = ({ column }) => {
<RoleSelector <RoleSelector
id={`${column.id}-role-filter`} id={`${column.id}-role-filter`}
value={columnFilterValue as TeamApplicationRole} value={columnFilterValue as TeamApplicationRole}
onChange={value => { column.setFilterValue(value) }} onChange={(value) => { column.setFilterValue(value) }}
allowEmpty allowEmpty
/> />
) )
@@ -36,7 +36,7 @@ const ApplicationsFilter: FC<ApplicationsFilterProps> = ({ column }) => {
fullWidth fullWidth
type="text" type="text"
value={(columnFilterValue ?? '') as string} value={(columnFilterValue ?? '') as string}
onChange={value => { column.setFilterValue(value) }} onChange={(value) => { column.setFilterValue(value) }}
placeholder="Buscar..." placeholder="Buscar..."
className="w-36 border shadow rounded" className="w-36 border shadow rounded"
list={column.id + 'list'} list={column.id + 'list'}
@@ -3,7 +3,7 @@ import { Table, TableBody, TableCell, TableContainer, TableHead, TableHeadCell,
import useManageError from '@/hooks/useManageError' import useManageError from '@/hooks/useManageError'
import { css } from '@/styled-system/css' import { css } from '@/styled-system/css'
import { formatDate } from '@/utilities/date' import { formatDate } from '@/utilities/date'
import { type TeamApplication, type TeamApplicationList } from '@/utilities/teamApplication' import type { TeamApplication, TeamApplicationList } from '@/utilities/teamApplication'
import { faChevronLeft, faChevronRight, faSort, faSortAsc, faSortDesc } from '@fortawesome/free-solid-svg-icons' import { faChevronLeft, faChevronRight, faSort, faSortAsc, faSortDesc } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { createColumnHelper, flexRender, getCoreRowModel, useReactTable, type ColumnFiltersState, type PaginationState, type RowData, type SortingState } from '@tanstack/react-table' import { createColumnHelper, flexRender, getCoreRowModel, useReactTable, type ColumnFiltersState, type PaginationState, type RowData, type SortingState } from '@tanstack/react-table'
@@ -29,7 +29,7 @@ const columns = [
columnHelper.accessor('status', { columnHelper.accessor('status', {
header: 'Estado', header: 'Estado',
cell: StatusUpdater, cell: StatusUpdater,
getUniqueValues () { getUniqueValues() {
return ['Pending', 'Accepted', 'Rejected'] return ['Pending', 'Accepted', 'Rejected']
} }
}), }),
@@ -125,7 +125,7 @@ const ApplicationsList: FC = () => {
if (error instanceof Error && error.name === 'AbortError') return if (error instanceof Error && error.name === 'AbortError') return
manageError(error, 'Error al obtener las aplicaciones', 'Error desconocido al obtener las aplicaciones', 'error') manageError(error, 'Error al obtener las aplicaciones', 'Error desconocido al obtener las aplicaciones', 'error')
}) })
}, [pagination, sorting, columnFilters]) }, [pagination, sorting, columnFilters, manageError])
// TODO: Better UI Controls for: column visibility. Quantity selector. // TODO: Better UI Controls for: column visibility. Quantity selector.
return ( return (
@@ -145,7 +145,9 @@ const ApplicationsList: FC = () => {
id={`${column.id}-view`} id={`${column.id}-view`}
checked={column.getIsVisible()} checked={column.getIsVisible()}
onChange={column.getToggleVisibilityHandler()} onChange={column.getToggleVisibilityHandler()}
/> {column.columnDef.header?.toString() ?? column.id} />
{' '}
{column.columnDef.header?.toString() ?? column.id}
</label> </label>
</div> </div>
))} ))}
@@ -159,8 +161,8 @@ const ApplicationsList: FC = () => {
<TableHeadCell <TableHeadCell
key={header.id} key={header.id}
className={css({ className={css({
verticalAlign: 'top', 'verticalAlign': 'top',
position: 'relative', 'position': 'relative',
'&:hover > [data-is-resizing]': { '&:hover > [data-is-resizing]': {
backgroundColor: 'border' backgroundColor: 'border'
} }
@@ -187,27 +189,27 @@ const ApplicationsList: FC = () => {
size="small" size="small"
onClick={header.column.getToggleSortingHandler()} onClick={header.column.getToggleSortingHandler()}
> >
<FontAwesomeIcon icon={header.column.getIsSorted() === 'asc' ? faSortAsc : header.column.getIsSorted() === 'desc' ? faSortDesc : faSort} size="sm" fixedWidth /> <FontAwesomeIcon icon={header.column.getIsSorted() === 'asc' ? faSortAsc : header.column.getIsSorted() === 'desc' ? faSortDesc : faSort} size="sm" />
</IconButton> </IconButton>
)} )}
</div> </div>
{header.column.getCanFilter() {header.column.getCanFilter()
? ( ? (
<ApplicationsFilter column={header.column}/> <ApplicationsFilter column={header.column} />
) )
: null : null}
}
</div> </div>
<div <button
type="button"
className={css({ className={css({
position: 'absolute', 'position': 'absolute',
top: 0, 'top': 0,
right: 0, 'right': 0,
height: '100%', 'height': '100%',
width: '5px', 'width': '5px',
cursor: 'col-resize', 'cursor': 'col-resize',
userSelect: 'none', 'userSelect': 'none',
touchAction: 'none', 'touchAction': 'none',
'&:hover': { '&:hover': {
backgroundColor: 'border' backgroundColor: 'border'
}, },
@@ -255,14 +257,20 @@ const ApplicationsList: FC = () => {
onClick={() => { table.previousPage() }} onClick={() => { table.previousPage() }}
disabled={!table.getCanPreviousPage()} disabled={!table.getCanPreviousPage()}
> >
<FontAwesomeIcon icon={faChevronLeft} fixedWidth /> <FontAwesomeIcon icon={faChevronLeft} />
</IconButton> </IconButton>
Pagina {table.getState().pagination.pageIndex + 1} de {table.getPageCount()} Pagina
{' '}
{table.getState().pagination.pageIndex + 1}
{' '}
de
{' '}
{table.getPageCount()}
<IconButton <IconButton
onClick={() => { table.nextPage() }} onClick={() => { table.nextPage() }}
disabled={!table.getCanNextPage()} disabled={!table.getCanNextPage()}
> >
<FontAwesomeIcon icon={faChevronRight} fixedWidth /> <FontAwesomeIcon icon={faChevronRight} />
</IconButton> </IconButton>
</div> </div>
</> </>
@@ -15,17 +15,17 @@ const RoleSelector: FC<RoleSelectorProps> = ({ id, value, onChange, allowEmpty }
<select <select
id={`${id}-status`} id={`${id}-status`}
className={css({ className={css({
width: '100%', 'width': '100%',
border: 'none', 'border': 'none',
background: 'transparent', 'background': 'transparent',
color: 'inherit', 'color': 'inherit',
outline: 'none', 'outline': 'none',
cursor: 'pointer', 'cursor': 'pointer',
fontSize: 'inherit', 'fontSize': 'inherit',
fontWeight: 'inherit', 'fontWeight': 'inherit',
lineHeight: 'inherit', 'lineHeight': 'inherit',
padding: '0', 'padding': '0',
borderRadius: '0', 'borderRadius': '0',
'&:focus': { '&:focus': {
outline: 'none' outline: 'none'
} }
@@ -15,17 +15,17 @@ const StatusSelector: FC<StatusSelectorProps> = ({ id, value, onChange, allowEmp
<select <select
id={`${id}-status`} id={`${id}-status`}
className={css({ className={css({
width: '100%', 'width': '100%',
border: 'none', 'border': 'none',
background: 'transparent', 'background': 'transparent',
color: 'inherit', 'color': 'inherit',
outline: 'none', 'outline': 'none',
cursor: 'pointer', 'cursor': 'pointer',
fontSize: 'inherit', 'fontSize': 'inherit',
fontWeight: 'inherit', 'fontWeight': 'inherit',
lineHeight: 'inherit', 'lineHeight': 'inherit',
padding: '0', 'padding': '0',
borderRadius: '0', 'borderRadius': '0',
'&:focus': { '&:focus': {
outline: 'none' outline: 'none'
} }
+6 -9
View File
@@ -25,9 +25,9 @@ const getTeams = async (): Promise<GetTeamsResponse> => {
const moderatorMembers: Models.MembershipList = await getClanMembers(MODERATOR_CLAN_ID) const moderatorMembers: Models.MembershipList = await getClanMembers(MODERATOR_CLAN_ID)
const collaboratorMembers: Models.MembershipList = await getClanMembers(COLLABORATOR_CLAN_ID) const collaboratorMembers: Models.MembershipList = await getClanMembers(COLLABORATOR_CLAN_ID)
const adminsPromises = adminMembers.memberships.map(async membership => await getUser(membership.userId)) const adminsPromises = adminMembers.memberships.map(async (membership) => await getUser(membership.userId))
const moderatorsPromises = moderatorMembers.memberships.map(async membership => await getUser(membership.userId)) const moderatorsPromises = moderatorMembers.memberships.map(async (membership) => await getUser(membership.userId))
const collaboratorsPromises = collaboratorMembers.memberships.map(async membership => await getUser(membership.userId)) const collaboratorsPromises = collaboratorMembers.memberships.map(async (membership) => await getUser(membership.userId))
const [admins, moderators, collaborators] = await Promise.all([ const [admins, moderators, collaborators] = await Promise.all([
Promise.all(adminsPromises), Promise.all(moderatorsPromises), Promise.all(collaboratorsPromises) Promise.all(adminsPromises), Promise.all(moderatorsPromises), Promise.all(collaboratorsPromises)
@@ -96,8 +96,7 @@ const EquipoPage: FC = async () => {
<Typography variant="body2" color="info"> <Typography variant="body2" color="info">
Ups, parece que ahora mismo no hay administradores, pero en EntGamers siempre estamos estamos buscando gente que quiera organizar cosas para la comunidad, puedes contactarnos para formar parte de nuestro equipo haciendo click en el siguiente enlace. Ups, parece que ahora mismo no hay administradores, pero en EntGamers siempre estamos estamos buscando gente que quiera organizar cosas para la comunidad, puedes contactarnos para formar parte de nuestro equipo haciendo click en el siguiente enlace.
</Typography> </Typography>
) )}
}
<div className={center()}> <div className={center()}>
<NextLink <NextLink
className={button({ color: 'info' })} className={button({ color: 'info' })}
@@ -158,8 +157,7 @@ const EquipoPage: FC = async () => {
<Typography variant="body2" color="info"> <Typography variant="body2" color="info">
Ups, parece que ahora mismo no hay moderadores, pero en EntGamers siempre estamos buscando gente que quiera ayudar a la comunidad. si quieres ser moderador, puedes hacer click en el botón de abajo. Ups, parece que ahora mismo no hay moderadores, pero en EntGamers siempre estamos buscando gente que quiera ayudar a la comunidad. si quieres ser moderador, puedes hacer click en el botón de abajo.
</Typography> </Typography>
) )}
}
<div className={center()}> <div className={center()}>
<NextLink <NextLink
className={button({ color: 'info' })} className={button({ color: 'info' })}
@@ -220,8 +218,7 @@ const EquipoPage: FC = async () => {
<Typography variant="body2" color="info"> <Typography variant="body2" color="info">
Ups, parece que ahora mismo no hay colaboradores, pero en EntGamers siempre estamos buscando gente que quiera ayudar a la comunidad. si quieres ser colaborador, puedes hacer click en el botón de abajo. Ups, parece que ahora mismo no hay colaboradores, pero en EntGamers siempre estamos buscando gente que quiera ayudar a la comunidad. si quieres ser colaborador, puedes hacer click en el botón de abajo.
</Typography> </Typography>
) )}
}
<div className={center()}> <div className={center()}>
<NextLink <NextLink
className={button({ color: 'info' })} className={button({ color: 'info' })}
+98 -60
View File
@@ -16,11 +16,14 @@ import { ADMIN_CLAN_ID, COLLABORATOR_CLAN_ID, MODERATOR_CLAN_ID } from 'entgamer
import { createTeamApplication, type TeamApplicationData } from 'entgamers-database/frontend/database/teamApplications' import { createTeamApplication, type TeamApplicationData } from 'entgamers-database/frontend/database/teamApplications'
import { useFormik } from 'formik' import { useFormik } from 'formik'
import { AnimatePresence, motion } from 'framer-motion' import { AnimatePresence, motion } from 'framer-motion'
import { useSearchParams } from 'next/navigation' import { usePathname, useSearchParams } from 'next/navigation'
import { useRouter } from 'next/router'
import { useEffect, type FC } from 'react' import { useEffect, type FC } from 'react'
const ApplyForm: FC = () => { const ApplyForm: FC = () => {
const searchParams = useSearchParams() const searchParams = useSearchParams()
const pathname = usePathname()
const router = useRouter()
const { manageError } = useManageError() const { manageError } = useManageError()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
@@ -54,8 +57,11 @@ const ApplyForm: FC = () => {
.catch((error) => { .catch((error) => {
console.error(error) console.error(error)
}) })
.finally(() => {
router.replace(pathname)
})
} }
}, []) }, [searchParams, formik, pathname, router])
return ( return (
<form <form
@@ -70,7 +76,7 @@ const ApplyForm: FC = () => {
})} })}
> >
<Button <Button
type='button' type="button"
onClick={() => { onClick={() => {
formik.setFieldValue('role', MODERATOR_CLAN_ID) formik.setFieldValue('role', MODERATOR_CLAN_ID)
.catch((error) => { .catch((error) => {
@@ -82,7 +88,7 @@ const ApplyForm: FC = () => {
Moderador Moderador
</Button> </Button>
<Button <Button
type='button' type="button"
onClick={() => { onClick={() => {
formik.setFieldValue('role', ADMIN_CLAN_ID) formik.setFieldValue('role', ADMIN_CLAN_ID)
.catch((error) => { .catch((error) => {
@@ -94,7 +100,7 @@ const ApplyForm: FC = () => {
Administrador Administrador
</Button> </Button>
<Button <Button
type='button' type="button"
onClick={() => { onClick={() => {
formik.setFieldValue('role', COLLABORATOR_CLAN_ID) formik.setFieldValue('role', COLLABORATOR_CLAN_ID)
.catch((error) => { .catch((error) => {
@@ -121,90 +127,87 @@ const ApplyForm: FC = () => {
})} })}
> >
<FormGroup> <FormGroup>
<label htmlFor='name'>Nombre</label> <label htmlFor="name">Nombre</label>
<Input <Input
id='name' id="name"
type='text' type="text"
value={formik.values.name} value={formik.values.name}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
/> />
{formik.touched.name !== undefined && formik.errors.name !== undefined {formik.touched.name !== undefined && formik.errors.name !== undefined
? ( ? (
<Typography variant='caption' color='danger'> <Typography variant="caption" color="danger">
{formik.errors.name} {formik.errors.name}
</Typography> </Typography>
) )
: ( : (
<Typography variant='caption' color='info'> <Typography variant="caption" color="info">
Tu nombre. Tu nombre.
</Typography> </Typography>
)} )}
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<label htmlFor='email'>Email</label> <label htmlFor="email">Email</label>
<Input <Input
id='email' id="email"
type='email' type="email"
value={formik.values.email} value={formik.values.email}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
/> />
{formik.touched.email !== undefined && formik.errors.email !== undefined {formik.touched.email !== undefined && formik.errors.email !== undefined
? ( ? (
<Typography variant='caption' color='danger'> <Typography variant="caption" color="danger">
{formik.errors.email} {formik.errors.email}
</Typography> </Typography>
) )
: ( : (
<Typography variant='caption' color='info'> <Typography variant="caption" color="info">
Tu email, para poder contactarte. Tu email, para poder contactarte.
</Typography> </Typography>
) )}
}
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<label htmlFor='discord'>Nombre de Discord</label> <label htmlFor="discord">Nombre de Discord</label>
<Input <Input
id='discord' id="discord"
type='text' type="text"
value={formik.values.discord} value={formik.values.discord}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
/> />
{formik.touched.discord !== undefined && formik.errors.discord !== undefined {formik.touched.discord !== undefined && formik.errors.discord !== undefined
? ( ? (
<Typography variant='caption' color='danger'> <Typography variant="caption" color="danger">
{formik.errors.discord} {formik.errors.discord}
</Typography> </Typography>
) )
: ( : (
<Typography variant='caption' color='info'> <Typography variant="caption" color="info">
Tu nombre de Discord, para poder contactarte. Tu nombre de Discord, para poder contactarte.
</Typography> </Typography>
) )}
}
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<label htmlFor='message'>Mensaje</label> <label htmlFor="message">Mensaje</label>
<TextArea <TextArea
id='message' id="message"
value={formik.values.message} value={formik.values.message}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
/> />
{formik.touched.message !== undefined && formik.errors.message !== undefined {formik.touched.message !== undefined && formik.errors.message !== undefined
? ( ? (
<Typography variant='caption' color='danger'> <Typography variant="caption" color="danger">
{formik.errors.message} {formik.errors.message}
</Typography> </Typography>
) )
: ( : (
<Typography variant='caption' color='info'> <Typography variant="caption" color="info">
¿Por que te gustaría unirte al equipo?, ¿Que te gustaría hacer?, etc. ¿Por que te gustaría unirte al equipo?, ¿Que te gustaría hacer?, etc.
</Typography> </Typography>
) )}
}
</FormGroup> </FormGroup>
<div <div
className={css({ className={css({
@@ -212,7 +215,7 @@ const ApplyForm: FC = () => {
})} })}
> >
<Button <Button
type='submit' type="submit"
disabled={!formik.isValid || !formik.dirty} disabled={!formik.isValid || !formik.dirty}
fullWidth fullWidth
@@ -239,14 +242,13 @@ const ApplyForm: FC = () => {
justifyContent: 'center' justifyContent: 'center'
})} })}
> >
<Typography variant='h2' align="center">¡Gracias por interesarte en unirte al equipo!</Typography> <Typography variant="h2" align="center">¡Gracias por interesarte en unirte al equipo!</Typography>
<Typography variant='body1'> <Typography variant="body1">
El equipo de EntGamers se pondrá en contacto contigo a la brevedad posible. El equipo de EntGamers se pondrá en contacto contigo a la brevedad posible.
</Typography> </Typography>
</div> </div>
</div> </div>
) )}
}
<div <div
className={css({ className={css({
overflow: 'hidden', overflow: 'hidden',
@@ -254,31 +256,39 @@ const ApplyForm: FC = () => {
paddingBlock: 'medium' paddingBlock: 'medium'
})} })}
> >
<AnimatePresence mode='wait' initial={false}> <AnimatePresence mode="wait" initial={false}>
{formik.values.role === MODERATOR_CLAN_ID && ( {formik.values.role === MODERATOR_CLAN_ID && (
<motion.div <motion.div
key={'motion-moderator'} key="motion-moderator"
transition={{ duration: 0.15, ease: 'easeInOut' }} transition={{ duration: 0.15, ease: 'easeInOut' }}
initial={{ opacity: 0, x: '-250px' }} initial={{ opacity: 0, x: '-250px' }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: '250px' }} exit={{ opacity: 0, x: '250px' }}
> >
<Typography variant='h2' align="center">Moderadores</Typography> <Typography variant="h2" align="center">Moderadores</Typography>
<Typography variant='body1'> <Typography variant="body1">
El equipo de moderación de EntGamers se encarga de moderar los distintos espacios en los que se desenvuelve la comunidad, como los grupos de Facebook, Discord, Etc. El equipo de moderación de EntGamers se encarga de moderar los distintos espacios en los que se desenvuelve la comunidad, como los grupos de Facebook, Discord, Etc.
</Typography> </Typography>
<Typography variant='h3'>Requisitos</Typography> <Typography variant="h3">Requisitos</Typography>
<ul className="fa-ul"> <ul className="fa-ul">
<li> <li>
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} fixedWidth listItem /> <strong>Imparcialidad</strong> <span className="fa-li">
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} />
</span>
{' '}
<strong>Imparcialidad</strong>
<br /> <br />
La comunidad esta conformada por amigos y conocidos, por lo tanto es importante poder actuar de forma imparcial y responsable. La comunidad esta conformada por amigos y conocidos, por lo tanto es importante poder actuar de forma imparcial y responsable.
</li> </li>
</ul> </ul>
<Typography variant='h3'>Beneficios</Typography> <Typography variant="h3">Beneficios</Typography>
<ul className="fa-ul"> <ul className="fa-ul">
<li> <li>
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} fixedWidth listItem /> <strong>Experiencia</strong> <span className="fa-li">
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} />
</span>
{' '}
<strong>Experiencia</strong>
<br /> <br />
Uno de los objetivos de la comunidad es brindar experiencia en gestión y desarrollo de proyectos equiparable a un entorno laboral, que sea comprobable y útil. Uno de los objetivos de la comunidad es brindar experiencia en gestión y desarrollo de proyectos equiparable a un entorno laboral, que sea comprobable y útil.
</li> </li>
@@ -287,28 +297,36 @@ const ApplyForm: FC = () => {
)} )}
{formik.values.role === COLLABORATOR_CLAN_ID && ( {formik.values.role === COLLABORATOR_CLAN_ID && (
<motion.div <motion.div
key={'motion-collaborator'} key="motion-collaborator"
transition={{ duration: 0.15, ease: 'easeInOut' }} transition={{ duration: 0.15, ease: 'easeInOut' }}
initial={{ opacity: 0, x: '-250px' }} initial={{ opacity: 0, x: '-250px' }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: '250px' }} exit={{ opacity: 0, x: '250px' }}
> >
<Typography variant='h2' align='center'>Colaborador</Typography> <Typography variant="h2" align="center">Colaborador</Typography>
<Typography variant='body1'> <Typography variant="body1">
Los colaboradores son personas ajenas al staff central de EntGamers que nos ayudan a traer contenido, eventos y actividades a la comunidad. Los colaboradores son personas ajenas al staff central de EntGamers que nos ayudan a traer contenido, eventos y actividades a la comunidad.
</Typography> </Typography>
<Typography variant='h3'>Requisitos</Typography> <Typography variant="h3">Requisitos</Typography>
<ul className="fa-ul"> <ul className="fa-ul">
<li> <li>
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} fixedWidth listItem /> <strong>Profesionalismo</strong> <span className="fa-li">
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} />
</span>
{' '}
<strong>Profesionalismo</strong>
<br /> <br />
La comunidad siempre intenta conseguir el mayor nivel de calidad en todos sus proyectos, por lo que buscamos gente dispuesta a otorgar este nivel de profesionalismo para el disfrute de la comunidad. La comunidad siempre intenta conseguir el mayor nivel de calidad en todos sus proyectos, por lo que buscamos gente dispuesta a otorgar este nivel de profesionalismo para el disfrute de la comunidad.
</li> </li>
</ul> </ul>
<Typography variant='h3'>Beneficios</Typography> <Typography variant="h3">Beneficios</Typography>
<ul className="fa-ul"> <ul className="fa-ul">
<li> <li>
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} fixedWidth listItem /> <strong>Apoyo</strong> <span className="fa-li">
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} />
</span>
{' '}
<strong>Apoyo</strong>
<br /> <br />
Puedes contar con el apoyo de la comunidad para tus proyectos, ya sea en forma de difusión, asesoramiento o recursos. Puedes contar con el apoyo de la comunidad para tus proyectos, ya sea en forma de difusión, asesoramiento o recursos.
</li> </li>
@@ -317,43 +335,63 @@ const ApplyForm: FC = () => {
)} )}
{formik.values.role === ADMIN_CLAN_ID && ( {formik.values.role === ADMIN_CLAN_ID && (
<motion.div <motion.div
key={'motion-administrator'} key="motion-administrator"
transition={{ duration: 0.15, ease: 'easeInOut' }} transition={{ duration: 0.15, ease: 'easeInOut' }}
initial={{ opacity: 0, x: '-250px' }} initial={{ opacity: 0, x: '-250px' }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: '250px' }} exit={{ opacity: 0, x: '250px' }}
> >
<Typography variant='h2' align='center'>Administradores</Typography> <Typography variant="h2" align="center">Administradores</Typography>
<Typography variant='body1'> <Typography variant="body1">
Los administradores son quienes se encargan de que todo funcione como es debido en la comunidad, desde la moderación de los grupos hasta la organización de eventos y actividades. Son los responsables de que la comunidad siga creciendo y mejorando. Los administradores son quienes se encargan de que todo funcione como es debido en la comunidad, desde la moderación de los grupos hasta la organización de eventos y actividades. Son los responsables de que la comunidad siga creciendo y mejorando.
</Typography> </Typography>
<Typography variant='h3'>Requisitos</Typography> <Typography variant="h3">Requisitos</Typography>
<ul className="fa-ul"> <ul className="fa-ul">
<li> <li>
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} fixedWidth listItem /> <strong>Profesionalismo</strong> <span className="fa-li">
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} />
</span>
{' '}
<strong>Profesionalismo</strong>
<br /> <br />
La comunidad siempre intenta conseguir el mayor nivel de calidad en todos sus proyectos, por lo que buscamos gente dispuesta a otorgar este nivel de profesionalismo para el disfrute de la comunidad. La comunidad siempre intenta conseguir el mayor nivel de calidad en todos sus proyectos, por lo que buscamos gente dispuesta a otorgar este nivel de profesionalismo para el disfrute de la comunidad.
</li> </li>
<li> <li>
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} fixedWidth listItem /> <strong>Constancia</strong> <span className="fa-li">
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} />
</span>
{' '}
<strong>Constancia</strong>
<br /> <br />
La comunidad busca gente que en sus posibilidades sea activa, que pueda estar al tanto de lo que pasa en ella. La comunidad busca gente que en sus posibilidades sea activa, que pueda estar al tanto de lo que pasa en ella.
</li> </li>
<li> <li>
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} fixedWidth listItem /> <strong>Proactividad</strong> <span className="fa-li">
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} />
</span>
{' '}
<strong>Proactividad</strong>
<br /> <br />
La comunidad esta en constante crecimiento, por eso, buscamos gente que ayude a buscar nuevas oportunidades para diferentes proyectos y actividades de interés a la comunidad. La comunidad esta en constante crecimiento, por eso, buscamos gente que ayude a buscar nuevas oportunidades para diferentes proyectos y actividades de interés a la comunidad.
</li> </li>
</ul> </ul>
<Typography variant='h3'>Beneficios</Typography> <Typography variant="h3">Beneficios</Typography>
<ul className="fa-ul"> <ul className="fa-ul">
<li> <li>
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} fixedWidth listItem /> <strong>Experiencia</strong> <span className="fa-li">
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} />
</span>
{' '}
<strong>Experiencia</strong>
<br /> <br />
Uno de los objetivos de la comunidad es brindar experiencia en gestión y desarrollo de proyectos equiparable a un entorno laboral, que sea comprobable y útil. Uno de los objetivos de la comunidad es brindar experiencia en gestión y desarrollo de proyectos equiparable a un entorno laboral, que sea comprobable y útil.
</li> </li>
<li> <li>
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} fixedWidth listItem /> <strong>Capacitación</strong> <span className="fa-li">
<FontAwesomeIcon icon={faChevronRight as FontAwesomeIconProps['icon']} />
</span>
{' '}
<strong>Capacitación</strong>
<br /> <br />
La comunidad buscara dar capacitación a sus miembros en lo referido a herramientas y procedimientos utilizados. La comunidad buscara dar capacitación a sus miembros en lo referido a herramientas y procedimientos utilizados.
</li> </li>
+4 -2
View File
@@ -1,7 +1,7 @@
import Typography from '@/components/ui/Typography' import Typography from '@/components/ui/Typography'
import { Container } from '@/styled-system/jsx' import { Container } from '@/styled-system/jsx'
import { ensureTeamApplicationsCollection } from 'entgamers-database/backend/database/teamApplications' import { ensureTeamApplicationsCollection } from 'entgamers-database/backend/database/teamApplications'
import { type FC } from 'react' import { Suspense, type FC } from 'react'
import ApplyForm from './ApplyForm' import ApplyForm from './ApplyForm'
const EquipoUnirsePage: FC = async () => { const EquipoUnirsePage: FC = async () => {
@@ -12,7 +12,9 @@ const EquipoUnirsePage: FC = async () => {
<Typography variant="body1"> <Typography variant="body1">
El equipo de EntGamers está formado por personas que se dedican a la administración de la comunidad, a la organización de eventos y a la creación de contenido. Aquí podrás enterarte cuales son las funciones de cada uno de los miembros del equipo y como puedes unirte a nosotros. El equipo de EntGamers está formado por personas que se dedican a la administración de la comunidad, a la organización de eventos y a la creación de contenido. Aquí podrás enterarte cuales son las funciones de cada uno de los miembros del equipo y como puedes unirte a nosotros.
</Typography> </Typography>
<ApplyForm /> <Suspense fallback={<Typography variant="body1">Cargando...</Typography>}>
<ApplyForm />
</Suspense>
</Container> </Container>
) )
} }
+2 -2
View File
@@ -8,8 +8,8 @@ import '@fontsource/open-sans/latin-700.css'
import '@fontsource/permanent-marker/latin-400.css' import '@fontsource/permanent-marker/latin-400.css'
import { config } from '@fortawesome/fontawesome-svg-core' import { config } from '@fortawesome/fontawesome-svg-core'
import '@fortawesome/fontawesome-svg-core/styles.css' import '@fortawesome/fontawesome-svg-core/styles.css'
import { type Metadata } from 'next' import type { Metadata } from 'next'
import { type FC, type ReactNode } from 'react' import type { FC, ReactNode } from 'react'
import FeedbackConsumer from './FeedbackConsumer' import FeedbackConsumer from './FeedbackConsumer'
import SessionConsumer from './SessionConsumer' import SessionConsumer from './SessionConsumer'
import StateProvider from './StateProvider' import StateProvider from './StateProvider'
+4 -2
View File
@@ -57,7 +57,7 @@ const LoginForm: FC = () => {
if (session.status === 'idle' && session.session !== undefined) { if (session.status === 'idle' && session.session !== undefined) {
router.push('/') router.push('/')
} }
}, [session]) }, [session, router])
return ( return (
<form <form
@@ -95,7 +95,9 @@ const LoginForm: FC = () => {
)} )}
</FormGroup> </FormGroup>
<Typography variant="caption" color="muted"> <Typography variant="caption" color="muted">
¿Perdiste tu contraseña? <NextLink href="/recover-password">Recupérala</NextLink> ¿Perdiste tu contraseña?
{' '}
<NextLink href="/recover-password">Recupérala</NextLink>
</Typography> </Typography>
<FormGroup> <FormGroup>
<Button <Button
+5 -4
View File
@@ -15,8 +15,7 @@ const LoginPage: FC = () => {
container(), container(),
css({ css({
minHeight: 'calc(100vh - 60px - 72px)' minHeight: 'calc(100vh - 60px - 72px)'
})) }))}
}
> >
<div <div
className={cx( className={cx(
@@ -41,8 +40,10 @@ const LoginPage: FC = () => {
className={card().content} className={card().content}
> >
<LoginForm /> <LoginForm />
<Typography variant="caption" align="center" > <Typography variant="caption" align="center">
¿No tienes una cuenta? <NextLink href="/register">Regístrate</NextLink> ¿No tienes una cuenta?
{' '}
<NextLink href="/register">Regístrate</NextLink>
</Typography> </Typography>
</div> </div>
</div> </div>
@@ -68,10 +68,11 @@ const CreateRecoverPasswordForm: FC = () => {
{formik.errors.email} {formik.errors.email}
</Typography> </Typography>
) )
: (<Typography variant="caption" color="info"> : (
Por favor, introduce el correo electrónico con el que te has registrado. Te enviaremos un correo con instrucciones para la recuperación de contraseña <Typography variant="caption" color="info">
</Typography>) Por favor, introduce el correo electrónico con el que te has registrado. Te enviaremos un correo con instrucciones para la recuperación de contraseña
} </Typography>
)}
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<Button <Button
@@ -1,27 +1,23 @@
'use client' 'use client'
import CreateRecoverPasswordForm from '@/app/recover-password/CreateRecoverPasswordForm' import CreateRecoverPasswordForm from '@/app/recover-password/CreateRecoverPasswordForm'
import { useSearchParams } from 'next/navigation' import { useSearchParams } from 'next/navigation'
import { useEffect, useState, type FC } from 'react' import type { FC } from 'react'
import UpdateRecoverPasswordForm, { type UpdateRecoverPasswordFormProps } from './UpdateRecoverPasswordForm' import UpdateRecoverPasswordForm, { type UpdateRecoverPasswordFormProps } from './UpdateRecoverPasswordForm'
const ManageRecoverPassword: FC = () => { const ManageRecoverPassword: FC = () => {
const [recoverData, setRecoverData] = useState<UpdateRecoverPasswordFormProps | undefined>()
const searchParams = useSearchParams() const searchParams = useSearchParams()
const userId = searchParams.get('userId')
useEffect(() => { const secret = searchParams.get('secret')
const userId = searchParams.get('userId') const recoverData: UpdateRecoverPasswordFormProps | undefined = (userId !== null && secret !== null) ? { userId, secret } : undefined
const secret = searchParams.get('secret')
if (userId !== null && secret !== null) {
setRecoverData({ userId, secret })
}
}, [])
if (recoverData === undefined) { if (recoverData === undefined) {
return <CreateRecoverPasswordForm /> return <CreateRecoverPasswordForm />
} else { } else {
return <UpdateRecoverPasswordForm return (
{...recoverData} <UpdateRecoverPasswordForm
/> {...recoverData}
/>
)
} }
} }
@@ -89,8 +89,7 @@ const UpdateRecoverPasswordForm: FC<UpdateRecoverPasswordFormProps> = (props) =>
<Typography variant="caption" color="info"> <Typography variant="caption" color="info">
Escribe tu nueva contraseña Escribe tu nueva contraseña
</Typography> </Typography>
) )}
}
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<label htmlFor="confirmPassword"> <label htmlFor="confirmPassword">
+5 -4
View File
@@ -4,7 +4,7 @@ import { css, cx } from '@/styled-system/css'
import { Center } from '@/styled-system/jsx' import { Center } from '@/styled-system/jsx'
import { container } from '@/styled-system/patterns' import { container } from '@/styled-system/patterns'
import { card } from '@/styled-system/recipes' import { card } from '@/styled-system/recipes'
import { type FC } from 'react' import { Suspense, type FC } from 'react'
const page: FC = () => { const page: FC = () => {
return ( return (
@@ -13,8 +13,7 @@ const page: FC = () => {
container(), container(),
css({ css({
minHeight: 'calc(100vh - 60px - 72px)' minHeight: 'calc(100vh - 60px - 72px)'
})) }))}
}
> >
<div <div
className={cx( className={cx(
@@ -36,7 +35,9 @@ const page: FC = () => {
<div <div
className={card().content} className={card().content}
> >
<ManageRecoverPassword /> <Suspense fallback={<Typography variant="body1">Cargando...</Typography>}>
<ManageRecoverPassword />
</Suspense>
</div> </div>
</div> </div>
</Center> </Center>
+5 -4
View File
@@ -15,8 +15,7 @@ const RegisterPage: FC = () => {
container(), container(),
css({ css({
minHeight: 'calc(100vh - 60px - 72px)' minHeight: 'calc(100vh - 60px - 72px)'
})) }))}
}
> >
<div <div
@@ -42,8 +41,10 @@ const RegisterPage: FC = () => {
className={card().content} className={card().content}
> >
<RegisterForm /> <RegisterForm />
<Typography variant="caption" align="center" > <Typography variant="caption" align="center">
¿Ya tienes una cuenta? <NextLink href="/login">Inicia sesión</NextLink> ¿Ya tienes una cuenta?
{' '}
<NextLink href="/login">Inicia sesión</NextLink>
</Typography> </Typography>
</div> </div>
</div> </div>
+23 -7
View File
@@ -4,7 +4,7 @@ import { Container } from '@/styled-system/jsx'
import { faChevronRight, faHeart } from '@fortawesome/free-solid-svg-icons' import { faChevronRight, faHeart } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import NextLink from 'next/link' import NextLink from 'next/link'
import { type FC } from 'react' import type { FC } from 'react'
const Footer: FC = () => { const Footer: FC = () => {
return ( return (
@@ -22,14 +22,24 @@ const Footer: FC = () => {
})} })}
> >
<div> <div>
<Typography variant="h3" component='div'> Acerca de </Typography> <Typography variant="h3" component="div"> Acerca de </Typography>
<ul className="fa-ul"> <ul className="fa-ul">
<li><FontAwesomeIcon icon={faChevronRight} listItem fixedWidth /><NextLink href="/acerca-de"> EntGamers</NextLink></li> <li>
<li><FontAwesomeIcon icon={faChevronRight} listItem fixedWidth /><NextLink href="/clanes"> Clanes</NextLink></li> <span className="fa-li">
<FontAwesomeIcon icon={faChevronRight} />
</span>
<NextLink href="/acerca-de"> EntGamers</NextLink>
</li>
<li>
<span className="fa-li">
<FontAwesomeIcon icon={faChevronRight} />
</span>
<NextLink href="/clanes"> Clanes</NextLink>
</li>
</ul> </ul>
</div> </div>
<div> <div>
<Typography variant="h3" component='div'> Contacto </Typography> <Typography variant="h3" component="div"> Contacto </Typography>
</div> </div>
<div></div> <div></div>
</Container> </Container>
@@ -40,8 +50,14 @@ const Footer: FC = () => {
justifyContent: 'center' justifyContent: 'center'
})} })}
> >
<Typography variant="body2" component='div'> <Typography variant="body2" component="div">
Hecho con <FontAwesomeIcon className={css({ color: 'red' })} icon={faHeart} /> por <a href="https://srjuggernaut.dev">SrJuggernaut</a> Hecho con
{' '}
<FontAwesomeIcon className={css({ color: 'red' })} icon={faHeart} />
{' '}
por
{' '}
<a href="https://srjuggernaut.dev">SrJuggernaut</a>
</Typography> </Typography>
</Container> </Container>
</footer> </footer>
+17 -17
View File
@@ -21,28 +21,28 @@ const Header: FC = () => {
return () => { return () => {
window.removeEventListener('scroll', handleScroll) window.removeEventListener('scroll', handleScroll)
} }
}, []) }, [handleScroll])
return ( return (
<> <>
<header <header
className={css({ className={css({
display: 'flex', 'display': 'flex',
alignItems: 'center', 'alignItems': 'center',
justifyContent: 'center', 'justifyContent': 'center',
backgroundColor: 'transparent', 'backgroundColor': 'transparent',
color: 'text', 'color': 'text',
minHeight: '60px', 'minHeight': '60px',
position: 'fixed', 'position': 'fixed',
top: 0, 'top': 0,
left: 0, 'left': 0,
width: '100%', 'width': '100%',
zIndex: 'sticky', 'zIndex': 'sticky',
boxShadow: 'none', 'boxShadow': 'none',
transitionProperty: 'background-color, box-shadow', 'transitionProperty': 'background-color, box-shadow',
transitionDuration: '0.25s', 'transitionDuration': '0.25s',
transitionTimingFunction: 'easeInOut', 'transitionTimingFunction': 'easeInOut',
willChange: 'background-color, box-shadow', 'willChange': 'background-color, box-shadow',
'&[data-scrolled=true]': { '&[data-scrolled=true]': {
backgroundColor: 'surface', backgroundColor: 'surface',
boxShadow: '2px 2px 4px 0px rgba(0, 0, 0, 0.25)' boxShadow: '2px 2px 4px 0px rgba(0, 0, 0, 0.25)'
+24 -26
View File
@@ -11,10 +11,10 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { ADMIN_CLAN_ID, MODERATOR_CLAN_ID } from 'entgamers-database/frontend/clanes/administrative' import { ADMIN_CLAN_ID, MODERATOR_CLAN_ID } from 'entgamers-database/frontend/clanes/administrative'
import { logout } from 'entgamers-database/frontend/session' import { logout } from 'entgamers-database/frontend/session'
import NextLink from 'next/link' import NextLink from 'next/link'
import { type FC } from 'react' import type { FC } from 'react'
const SessionButtons: FC = () => { const SessionButtons: FC = () => {
const { session, status, clanes } = useAppSelector(state => state.session) const { session, status, clanes } = useAppSelector((state) => state.session)
const { manageError } = useManageError() const { manageError } = useManageError()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
@@ -22,21 +22,19 @@ const SessionButtons: FC = () => {
if (status === 'idle' && session === undefined) { if (status === 'idle' && session === undefined) {
return ( return (
<> <Tooltip
<Tooltip title="Iniciar sesión"
title={'Iniciar sesión'} position="bottom"
position='bottom' >
<NextLink
href="/login"
className={
iconButton()
}
> >
<NextLink <FontAwesomeIcon icon={faUser} />
href="/login" </NextLink>
className={ </Tooltip>
iconButton()
}
>
<FontAwesomeIcon icon={faUser} fixedWidth />
</NextLink>
</Tooltip>
</>
) )
} }
@@ -44,8 +42,8 @@ const SessionButtons: FC = () => {
return ( return (
<> <>
<Tooltip <Tooltip
title={'Mi cuenta'} title="Mi cuenta"
position='bottom' position="bottom"
> >
<NextLink <NextLink
href="/cuenta" href="/cuenta"
@@ -53,13 +51,13 @@ const SessionButtons: FC = () => {
iconButton() iconButton()
} }
> >
<FontAwesomeIcon icon={faUser} fixedWidth /> <FontAwesomeIcon icon={faUser} />
</NextLink> </NextLink>
</Tooltip> </Tooltip>
{clanes !== undefined && clanes?.teams.some(team => team.$id === ADMIN_CLAN_ID || team.$id === MODERATOR_CLAN_ID) && ( {clanes?.teams.some((team) => team.$id === ADMIN_CLAN_ID || team.$id === MODERATOR_CLAN_ID) && (
<Tooltip <Tooltip
title={'Panel de administración'} title="Panel de administración"
position='bottom' position="bottom"
> >
<NextLink <NextLink
href="/dashboard" href="/dashboard"
@@ -67,13 +65,13 @@ const SessionButtons: FC = () => {
iconButton() iconButton()
} }
> >
<FontAwesomeIcon icon={faCogs} fixedWidth /> <FontAwesomeIcon icon={faCogs} />
</NextLink> </NextLink>
</Tooltip> </Tooltip>
)} )}
<Tooltip <Tooltip
title={'Cerrar sesión'} title="Cerrar sesión"
position='bottom' position="bottom"
> >
<IconButton <IconButton
onClick={() => { onClick={() => {
@@ -91,7 +89,7 @@ const SessionButtons: FC = () => {
}) })
}} }}
> >
<FontAwesomeIcon icon={faRightFromBracket} fixedWidth /> <FontAwesomeIcon icon={faRightFromBracket} />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
</> </>
+18 -15
View File
@@ -3,7 +3,7 @@ import BackDrop from '@/components/ui/BackDrop'
import IconButton from '@/components/ui/IconButton' import IconButton from '@/components/ui/IconButton'
import { css } from '@/styled-system/css' import { css } from '@/styled-system/css'
import { iconButton } from '@/styled-system/recipes' import { iconButton } from '@/styled-system/recipes'
import { type IconDefinition } from '@fortawesome/fontawesome-common-types' import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
import { faBars, faHome, faTimes, faUsers } from '@fortawesome/free-solid-svg-icons' import { faBars, faHome, faTimes, faUsers } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import NextLink from 'next/link' import NextLink from 'next/link'
@@ -62,6 +62,7 @@ const Menu: FC = () => {
})} })}
> >
<button <button
type="button"
className={iconButton({ className={iconButton({
color: 'danger' color: 'danger'
})} })}
@@ -80,22 +81,21 @@ const Menu: FC = () => {
> >
{menuLinks.map((menuLink, index) => ( {menuLinks.map((menuLink, index) => (
<li <li
key={`menu-link-${index}`} key={`menu-link-${menuLink.label}-${index.toString()}`}
onClick={() => { setIsMenuOpen(false) }}
> >
<NextLink <NextLink
className={css({ className={css({
display: 'flex', 'display': 'flex',
alignItems: 'center', 'alignItems': 'center',
justifyContent: 'left', 'justifyContent': 'left',
padding: '1rem', 'padding': '1rem',
textDecoration: 'none', 'textDecoration': 'none',
backgroundColor: 'transparent', 'backgroundColor': 'transparent',
color: 'text', 'color': 'text',
transitionProperty: 'background-color', 'transitionProperty': 'background-color',
transitionDuration: 'normal', 'transitionDuration': 'normal',
transitionTimingFunction: 'easeInOut', 'transitionTimingFunction': 'easeInOut',
willChange: 'background-color color', 'willChange': 'background-color color',
'&:hover': { '&:hover': {
backgroundColor: 'primary', backgroundColor: 'primary',
color: 'primary.contrast' color: 'primary.contrast'
@@ -107,8 +107,11 @@ const Menu: FC = () => {
})} })}
href={menuLink.href} href={menuLink.href}
data-active={pathName === menuLink.href} data-active={pathName === menuLink.href}
onClick={() => { setIsMenuOpen(false) }}
> >
<FontAwesomeIcon icon={menuLink.icon} fixedWidth />&nbsp;{menuLink.label} <FontAwesomeIcon icon={menuLink.icon} />
&nbsp;
{menuLink.label}
</NextLink> </NextLink>
</li> </li>
))} ))}
+4 -5
View File
@@ -1,9 +1,9 @@
import { cx } from '@/styled-system/css' import { cx } from '@/styled-system/css'
import { alert, type AlertVariantProps } from '@/styled-system/recipes/alert' import { alert, type AlertVariantProps } from '@/styled-system/recipes/alert'
import { type MergeOmitting } from '@/types/utilities' import type { MergeOmitting } from '@/types/utilities'
import { faTimes } from '@fortawesome/free-solid-svg-icons/faTimes' import { faTimes } from '@fortawesome/free-solid-svg-icons/faTimes'
import { FontAwesomeIcon, type FontAwesomeIconProps } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon, type FontAwesomeIconProps } from '@fortawesome/react-fontawesome'
import { type DetailedHTMLProps, type FC, type HTMLAttributes, type ReactNode } from 'react' import type { DetailedHTMLProps, FC, HTMLAttributes, ReactNode } from 'react'
import IconButton, { type IconButtonProps } from './IconButton' import IconButton, { type IconButtonProps } from './IconButton'
type ComposedAlertProps = MergeOmitting<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, AlertVariantProps> type ComposedAlertProps = MergeOmitting<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, AlertVariantProps>
@@ -32,9 +32,8 @@ export const AlertCloseButton: FC<ComposedAlertCloseButtonProps> = ({ children,
{...allOtherAlertProps} {...allOtherAlertProps}
> >
{children === undefined {children === undefined
? <FontAwesomeIcon icon={faTimes as FontAwesomeIconProps['icon']} fixedWidth size='sm' /> ? <FontAwesomeIcon icon={faTimes as FontAwesomeIconProps['icon']} size="sm" />
: children : children}
}
</IconButton> </IconButton>
) )
} }
+1 -2
View File
@@ -38,8 +38,7 @@ const BackDrop: FC<BackDropProps> = ({ isOpen, onClickAway, children }) => {
{children} {children}
</motion.div> </motion.div>
) )
: undefined : undefined}
}
</AnimatePresence> </AnimatePresence>
), document.body) ), document.body)
} }
+3 -5
View File
@@ -1,11 +1,9 @@
import { cx } from '@/styled-system/css' import { cx } from '@/styled-system/css'
import { button, type ButtonVariantProps } from '@/styled-system/recipes/button' import { button, type ButtonVariantProps } from '@/styled-system/recipes/button'
import { type MergeOmitting } from '@/types/utilities' import type { MergeOmitting } from '@/types/utilities'
import { type ButtonHTMLAttributes, type DetailedHTMLProps, type FC } from 'react' import type { ButtonHTMLAttributes, DetailedHTMLProps, FC } from 'react'
type ComposedButtonProps = MergeOmitting<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, ButtonVariantProps> export type ButtonProps = MergeOmitting<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, ButtonVariantProps>
export interface ButtonProps extends ComposedButtonProps {}
const Button: FC<ButtonProps> = ({ children, className, ...rest }) => { const Button: FC<ButtonProps> = ({ children, className, ...rest }) => {
const [buttonRecipeArgs, allOtherButtonProps] = button.splitVariantProps(rest) const [buttonRecipeArgs, allOtherButtonProps] = button.splitVariantProps(rest)
+3 -5
View File
@@ -1,11 +1,9 @@
import { cx } from '@/styled-system/css' import { cx } from '@/styled-system/css'
import { iconButton, type IconButtonVariantProps } from '@/styled-system/recipes/icon-button' import { iconButton, type IconButtonVariantProps } from '@/styled-system/recipes/icon-button'
import { type MergeOmitting } from '@/types/utilities' import type { MergeOmitting } from '@/types/utilities'
import { type ButtonHTMLAttributes, type DetailedHTMLProps, type FC } from 'react' import type { ButtonHTMLAttributes, DetailedHTMLProps, FC } from 'react'
type ComposedIconButtonProps = MergeOmitting<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, IconButtonVariantProps> export type IconButtonProps = MergeOmitting<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, IconButtonVariantProps>
export interface IconButtonProps extends ComposedIconButtonProps {}
const IconButton: FC<IconButtonProps> = ({ children, className, ...rest }) => { const IconButton: FC<IconButtonProps> = ({ children, className, ...rest }) => {
const [iconButtonRecipeArgs, allOtherIconButtonProps] = iconButton.splitVariantProps(rest) const [iconButtonRecipeArgs, allOtherIconButtonProps] = iconButton.splitVariantProps(rest)
+9 -10
View File
@@ -1,7 +1,7 @@
import { cx } from '@/styled-system/css' import { cx } from '@/styled-system/css'
import { typography, type TypographyVariantProps } from '@/styled-system/recipes/typography' import { typography, type TypographyVariantProps } from '@/styled-system/recipes/typography'
import { type MergeOmitting } from '@/types/utilities' import type { MergeOmitting } from '@/types/utilities'
import { type ElementType, type FC, type HTMLAttributes } from 'react' import { type ElementType, type FC, type HTMLAttributes, createElement } from 'react'
type ComposedTypographyProps = MergeOmitting<HTMLAttributes<HTMLElement>, TypographyVariantProps> type ComposedTypographyProps = MergeOmitting<HTMLAttributes<HTMLElement>, TypographyVariantProps>
@@ -35,19 +35,18 @@ const variantToComponent = (variant: TypographyVariantProps['variant']): Element
const Typography: FC<TypographyProps> = ({ children, className, component, ...rest }) => { const Typography: FC<TypographyProps> = ({ children, className, component, ...rest }) => {
const [typographyRecipeArgs, allOtherTypographyProps] = typography.splitVariantProps(rest) const [typographyRecipeArgs, allOtherTypographyProps] = typography.splitVariantProps(rest)
const Component = component ?? variantToComponent(typographyRecipeArgs.variant)
typographyRecipeArgs.color = typographyRecipeArgs.color ?? ( typographyRecipeArgs.color = typographyRecipeArgs.color ?? (
typeof typographyRecipeArgs.variant === 'string' && typographyRecipeArgs.variant.startsWith('h') typeof typographyRecipeArgs.variant === 'string' && typographyRecipeArgs.variant.startsWith('h')
? 'primary' ? 'primary'
: 'inherit' : 'inherit'
) )
return ( return createElement(
<Component component ?? variantToComponent(typographyRecipeArgs.variant),
className={cx(typography(typographyRecipeArgs), className)} {
{...allOtherTypographyProps} className: cx(typography(typographyRecipeArgs), className),
> ...allOtherTypographyProps
{children} },
</Component> children
) )
} }
+5 -3
View File
@@ -19,14 +19,16 @@ const DebouncedInput: FC<DebouncedInputProps> = ({ value: initialValue, onChange
onChange(value) onChange(value)
}, debounce) }, debounce)
return () => { clearTimeout(timeout) } return () => {
}, [value]) clearTimeout(timeout)
}
}, [value, onChange, debounce])
return ( return (
<Input <Input
{...props} {...props}
value={value} value={value}
onChange={e => { setValue(e.target.value) }} onChange={(e) => { setValue(e.target.value) }}
/> />
) )
} }
+5 -9
View File
@@ -1,21 +1,17 @@
import { cx } from '@/styled-system/css' import { cx } from '@/styled-system/css'
import { input, type InputVariantProps } from '@/styled-system/recipes/input' import { input, type InputVariantProps } from '@/styled-system/recipes/input'
import { type MergeOmitting } from '@/types/utilities' import type { MergeOmitting } from '@/types/utilities'
import { type DetailedHTMLProps, type FC, type InputHTMLAttributes } from 'react' import type { DetailedHTMLProps, FC, InputHTMLAttributes } from 'react'
type ComposedInputProps = MergeOmitting<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, InputVariantProps> export type InputProps = MergeOmitting<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, InputVariantProps>
export interface InputProps extends ComposedInputProps {} const Input: FC<InputProps> = ({ className, ...rest }) => {
const Input: FC<InputProps> = ({ children, className, ...rest }) => {
const [inputRecipeArgs, allOtherInputProps] = input.splitVariantProps(rest) const [inputRecipeArgs, allOtherInputProps] = input.splitVariantProps(rest)
return ( return (
<input <input
className={cx(input(inputRecipeArgs), className)} className={cx(input(inputRecipeArgs), className)}
{...allOtherInputProps} {...allOtherInputProps}
> />
{children}
</input>
) )
} }
+2 -2
View File
@@ -2,7 +2,7 @@ import IconButton from '@/components/ui/IconButton'
import Tooltip from '@/components/ui/Tooltip' import Tooltip from '@/components/ui/Tooltip'
import { css, cx } from '@/styled-system/css' import { css, cx } from '@/styled-system/css'
import { input, type InputVariantProps } from '@/styled-system/recipes/input' import { input, type InputVariantProps } from '@/styled-system/recipes/input'
import { type MergeOmitting } from '@/types/utilities' import type { MergeOmitting } from '@/types/utilities'
import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons' import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { useState, type FC, type InputHTMLAttributes } from 'react' import { useState, type FC, type InputHTMLAttributes } from 'react'
@@ -47,7 +47,7 @@ const PasswordInput: FC<InputProps> = ({ className, ...props }) => {
size="small" size="small"
onClick={() => { setShowPassword(!showPassword) }} onClick={() => { setShowPassword(!showPassword) }}
> >
<FontAwesomeIcon icon={showPassword ? faEyeSlash : faEye} fixedWidth/> <FontAwesomeIcon icon={showPassword ? faEyeSlash : faEye} />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
</div> </div>
+3 -3
View File
@@ -13,16 +13,16 @@ const feedbackSlice = createSlice({
name: 'feedback', name: 'feedback',
initialState, initialState,
reducers: { reducers: {
addAlert (state, action: PayloadAction<Alert>) { addAlert(state, action: PayloadAction<Alert>) {
return { return {
...state, ...state,
alerts: [...state.alerts, action.payload] alerts: [...state.alerts, action.payload]
} }
}, },
removeAlert (state, action: PayloadAction<string>) { removeAlert(state, action: PayloadAction<string>) {
return { return {
...state, ...state,
alerts: state.alerts.filter(alert => alert.id !== action.payload) alerts: state.alerts.filter((alert) => alert.id !== action.payload)
} }
} }
} }
+10 -10
View File
@@ -1,15 +1,15 @@
import { createSlice, type PayloadAction } from '@reduxjs/toolkit' import { createSlice, type PayloadAction } from '@reduxjs/toolkit'
import { type Models } from 'appwrite' import type { Models } from 'appwrite'
import { type ClanList } from 'entgamers-database/frontend/clanes' import type { ClanList } from 'entgamers-database/frontend/clanes'
import { type User } from 'entgamers-database/frontend/session' import type { User } from 'entgamers-database/frontend/session'
export type SessionState = export type SessionState
| { = | {
status: 'idle' | 'loading' | 'initializing' status: 'idle' | 'loading' | 'initializing'
session?: Models.Session session?: Models.Session
user?: User user?: User
clanes?: ClanList clanes?: ClanList
} }
const initialState: SessionState = { const initialState: SessionState = {
status: 'initializing' status: 'initializing'
+6 -5
View File
@@ -12,11 +12,11 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noEmit": true, "noEmit": true,
"esModuleInterop": true, "esModuleInterop": true,
"module": "Node16", "module": "esnext",
"moduleResolution": "Node16", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "react-jsx",
"incremental": true, "incremental": true,
"baseUrl": "src", "baseUrl": "src",
"paths": { "paths": {
@@ -35,9 +35,10 @@
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx",
".next/types/**/*.ts", ".next/types/**/*.ts",
"next.config.js" "next.config.js",
".next/dev/types/**/*.ts"
], ],
"exclude": [ "exclude": [
"node_modules" "node_modules"
] ]
} }