A new start

This commit is contained in:
Laura Hausmann 2023-02-06 02:23:00 +01:00
commit 5e2d0ceaa6
Signed by: zotan
GPG key ID: D044E84C5BE01605
118 changed files with 76698 additions and 0 deletions

186
.gitignore vendored Normal file
View file

@ -0,0 +1,186 @@
/packages/
/_ReSharper.Caches/
# Created by https://www.toptal.com/developers/gitignore/api/rider,dotnetcore,jetbrains+all
# Edit at https://www.toptal.com/developers/gitignore?templates=rider,dotnetcore,jetbrains+all
### DotnetCore ###
# .NET Core build folders
bin/
obj/
# Common node modules locations
/node_modules
/wwwroot/node_modules
### JetBrains+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### JetBrains+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### Rider ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
# Generated files
# Sensitive or high-churn files
# Gradle
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
# Mongo Explorer plugin
# File-based project format
# IntelliJ
# mpeltonen/sbt-idea plugin
# JIRA plugin
# Cursive Clojure plugin
# Crashlytics plugin (for Android Studio and IntelliJ)
# Editor-based Rest Client
# Android studio 3.1+ serialized cache file
# End of https://www.toptal.com/developers/gitignore/api/rider,dotnetcore,jetbrains+all
# Created by https://www.toptal.com/developers/gitignore/api/macos
# Edit at https://www.toptal.com/developers/gitignore?templates=macos
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# End of https://www.toptal.com/developers/gitignore/api/macos
database.db

3
AfRApay.MateCard/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.pio
CMakeListsPrivate.txt
cmake-build-*/

View file

@ -0,0 +1,33 @@
# !!! WARNING !!! AUTO-GENERATED FILE, PLEASE DO NOT MODIFY IT AND USE
# https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags
#
# If you need to override existing CMake configuration or add extra,
# please create `CMakeListsUser.txt` in the root of project.
# The `CMakeListsUser.txt` will not be overwritten by PlatformIO.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER_WORKS 1)
set(CMAKE_CXX_COMPILER_WORKS 1)
project("AfRApay.MateCard" C CXX)
include(CMakeListsPrivate.txt)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/CMakeListsUser.txt)
include(CMakeListsUser.txt)
endif()
add_custom_target(
Production ALL
COMMAND platformio -c clion run "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
Debug ALL
COMMAND platformio -c clion debug "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_executable(Z_DUMMY_TARGET ${SRC_LIST})

View file

@ -0,0 +1,155 @@
#ifndef AFRAPAY_LOGOS_H
#define AFRAPAY_LOGOS_H
enum e_logo {
LOGO_AFRAPAY, LOGO_CONTACTLESS, LOGO_MATECARD, LOGO_PENDING, LOGO_SUCCESS, LOGO_FAILURE
};
#define contactless_width 88
#define contactless_height 52
static unsigned char contactless_bits[] = {
0x00, 0x00, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE0, 0x07, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C,
0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00,
0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00,
0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0,
0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x80, 0x01,
0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x03, 0x00,
0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x06, 0x00, 0x00,
0x60, 0x00, 0x00, 0x00, 0x06, 0x0F, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x20,
0x00, 0x00, 0x00, 0x0E, 0x1E, 0xE0, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00,
0x00, 0x00, 0x1E, 0x1E, 0xB0, 0x3F, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00,
0x00, 0x1E, 0x1E, 0x10, 0xE0, 0x33, 0x00, 0x00, 0x08, 0x00, 0x00, 0x1C,
0x3C, 0x3C, 0x18, 0x00, 0x66, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x3C, 0x3C,
0x3C, 0x08, 0x00, 0x46, 0x00, 0x00, 0x04, 0x00, 0x00, 0x3C, 0x3C, 0x3C,
0x08, 0x06, 0xFE, 0x01, 0x00, 0x06, 0x00, 0x30, 0x78, 0x78, 0x38, 0x88,
0x1F, 0xFE, 0x07, 0x00, 0x02, 0x00, 0x78, 0x78, 0x78, 0x78, 0x88, 0x70,
0x02, 0x0C, 0x00, 0x03, 0x00, 0xF0, 0x78, 0x78, 0x78, 0x8C, 0xC0, 0x03,
0x18, 0x00, 0x03, 0x00, 0xF0, 0xF0, 0x70, 0x78, 0x84, 0x00, 0x07, 0x10,
0x00, 0x03, 0x00, 0xE0, 0xF0, 0x70, 0x78, 0x84, 0x01, 0x0C, 0x20, 0x00,
0x03, 0x00, 0xE0, 0xF1, 0x70, 0x78, 0x04, 0x03, 0x30, 0x60, 0x00, 0x03,
0x00, 0xE0, 0xF1, 0x70, 0x78, 0x0C, 0x04, 0xC0, 0x40, 0x00, 0x03, 0x00,
0xE0, 0xF1, 0x70, 0x78, 0xF8, 0x1D, 0x80, 0x81, 0x00, 0x03, 0x00, 0xE0,
0xF1, 0x70, 0x78, 0x00, 0x3F, 0x00, 0x80, 0x01, 0x03, 0x00, 0xE0, 0xF0,
0x70, 0x78, 0x00, 0x68, 0x00, 0x00, 0x01, 0x03, 0x00, 0xF0, 0xF0, 0x70,
0x78, 0x00, 0xC4, 0x00, 0x00, 0x02, 0x03, 0x00, 0xF0, 0x70, 0x78, 0x78,
0x00, 0x0C, 0x03, 0x00, 0x06, 0x02, 0x00, 0x78, 0x78, 0x78, 0x78, 0x00,
0x18, 0x06, 0x00, 0x04, 0x06, 0x00, 0x30, 0x78, 0x78, 0x38, 0x00, 0x30,
0x0C, 0x00, 0x08, 0x04, 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x00, 0x60, 0x18,
0x00, 0x18, 0x0C, 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x00, 0x90, 0x31, 0x00,
0x10, 0x0C, 0x00, 0x00, 0x1C, 0x3C, 0x3C, 0x00, 0x10, 0x3F, 0x00, 0x20,
0x18, 0x00, 0x00, 0x00, 0x1E, 0x1E, 0x00, 0x10, 0x60, 0x00, 0x60, 0x30,
0x00, 0x00, 0x00, 0x1E, 0x1E, 0x00, 0x60, 0x40, 0x00, 0x40, 0x30, 0x00,
0x00, 0x00, 0x0E, 0x1E, 0x00, 0x80, 0xF3, 0x00, 0x80, 0x60, 0x00, 0x00,
0x00, 0x06, 0x0F, 0x00, 0x00, 0x9E, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
0x00, 0x0F, 0x00, 0x00, 0x06, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80,
0x07, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07,
0x00, 0x80, 0x01, 0x1C, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE0, 0x00, 0x30, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00,
0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xE0, 0x07, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,};
#define failure_width 40
#define failure_height 40
static unsigned char failure_bits[] = {
0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x0F, 0x00, 0x00, 0xFC,
0xFF, 0x3F, 0x00, 0x00, 0xFE, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0xFF, 0xFF,
0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0xFF, 0x07, 0xF0,
0xFF, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF,
0xFF, 0x1F, 0xFC, 0xFF, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, 0xFF, 0x3F,
0xFE, 0x7F, 0xFF, 0xFE, 0x7F, 0xFE, 0x3F, 0x7E, 0xFC, 0x7F, 0xFE, 0x1F,
0x3C, 0xF8, 0x7F, 0xFF, 0x0F, 0x18, 0xF0, 0xFF, 0xFF, 0x1F, 0x00, 0xF8,
0xFF, 0xFF, 0x3F, 0x00, 0xFC, 0xFF, 0xFF, 0x7F, 0x00, 0xFE, 0xFF, 0xFF,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x7F, 0x00,
0xFE, 0xFF, 0xFF, 0x3F, 0x00, 0xFC, 0xFF, 0xFF, 0x1F, 0x00, 0xF8, 0xFF,
0xFF, 0x0F, 0x18, 0xF0, 0xFF, 0xFE, 0x1F, 0x3C, 0xF8, 0x7F, 0xFE, 0x3F,
0x7E, 0xFC, 0x7F, 0xFE, 0x7F, 0xFF, 0xFE, 0x7F, 0xFC, 0xFF, 0xFF, 0xFF,
0x3F, 0xFC, 0xFF, 0xFF, 0xFF, 0x3F, 0xF8, 0xFF, 0xFF, 0xFF, 0x1F, 0xF0,
0xFF, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0xFF,
0xFF, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0xFF, 0x01,
0x00, 0xFE, 0xFF, 0x7F, 0x00, 0x00, 0xFC, 0xFF, 0x3F, 0x00, 0x00, 0xF0,
0xFF, 0x0F, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00,};
#define pending_width 40
#define pending_height 40
static unsigned char pending_bits[] = {
0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x0F, 0x00, 0x00, 0xFC,
0xFF, 0x3F, 0x00, 0x00, 0xFE, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0xFF, 0xFF,
0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0xFF, 0x07, 0xF0,
0xFF, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF,
0xFF, 0x1F, 0xFC, 0xFF, 0xE3, 0xFF, 0x3F, 0xFC, 0xFF, 0xC3, 0xFF, 0x3F,
0xFE, 0xFF, 0xC3, 0xFF, 0x7F, 0xFE, 0xFF, 0xC3, 0xFF, 0x7F, 0xFE, 0xFF,
0xC3, 0xFF, 0x7F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF,
0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF,
0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0x03,
0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFC, 0xFF, 0xFF, 0xFF, 0x03, 0xF0, 0xFF,
0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0xFE, 0xFF, 0x3F, 0xE0, 0x7F, 0xFE, 0xFF,
0x7F, 0xF0, 0x7F, 0xFE, 0xFF, 0xFF, 0xF1, 0x7F, 0xFC, 0xFF, 0xFF, 0xFF,
0x3F, 0xFC, 0xFF, 0xFF, 0xFF, 0x3F, 0xF8, 0xFF, 0xFF, 0xFF, 0x1F, 0xF0,
0xFF, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0xFF,
0xFF, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0xFF, 0x01,
0x00, 0xFE, 0xFF, 0x7F, 0x00, 0x00, 0xFC, 0xFF, 0x3F, 0x00, 0x00, 0xF0,
0xFF, 0x0F, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00,};
#define success_width 40
#define success_height 40
static unsigned char success_bits[] = {
0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x0F, 0x00, 0x00, 0xFC,
0xFF, 0x3F, 0x00, 0x00, 0xFE, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0xFF, 0xFF,
0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0xFF, 0x07, 0xF0,
0xFF, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF,
0xFF, 0x1F, 0xFC, 0xFF, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, 0xFF, 0x3F,
0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE, 0xFF, 0xFF, 0xF3, 0x7F, 0xFE, 0xFF,
0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0x7F, 0xE0, 0xFF, 0xFF, 0xFF, 0x3F, 0xE0,
0xFF, 0xFF, 0xFF, 0x3F, 0xF0, 0xFF, 0xFF, 0xFF, 0x1F, 0xF8, 0xFF, 0xFF,
0x8F, 0x0F, 0xFC, 0xFF, 0xFF, 0x07, 0x07, 0xFE, 0xFF, 0xFF, 0x07, 0x00,
0xFF, 0xFF, 0xFF, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0x1F, 0xC0, 0xFF, 0xFF,
0xFF, 0x3F, 0xE0, 0xFF, 0xFF, 0xFE, 0x7F, 0xF0, 0xFF, 0x7F, 0xFE, 0xFF,
0xF8, 0xFF, 0x7F, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0xFC, 0xFF, 0xFF, 0xFF,
0x3F, 0xFC, 0xFF, 0xFF, 0xFF, 0x3F, 0xF8, 0xFF, 0xFF, 0xFF, 0x1F, 0xF0,
0xFF, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0xFF,
0xFF, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0xFF, 0x01,
0x00, 0xFE, 0xFF, 0x7F, 0x00, 0x00, 0xFC, 0xFF, 0x3F, 0x00, 0x00, 0xF0,
0xFF, 0x0F, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00,};
#define matecard_width 65
#define matecard_height 40
static unsigned char matecard_bits[] = {
0x00, 0x80, 0xBE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xEB,
0x0F, 0x00, 0x55, 0x05, 0x00, 0x00, 0x00, 0xB8, 0x5E, 0x1B, 0x20, 0x88,
0x10, 0x00, 0x00, 0x00, 0xEE, 0xF7, 0xF6, 0x90, 0x22, 0x44, 0x00, 0x00,
0x00, 0xBB, 0xDA, 0xAF, 0x4A, 0x94, 0x12, 0x01, 0x00, 0x80, 0xDF, 0xFF,
0xFA, 0x12, 0x42, 0x44, 0x00, 0x00, 0xE0, 0x7A, 0x55, 0x57, 0x45, 0x09,
0x91, 0x02, 0x00, 0xA0, 0xAF, 0xFF, 0xBD, 0x2A, 0xA2, 0x24, 0x08, 0x00,
0xF0, 0xFA, 0xAA, 0x57, 0x95, 0x08, 0x92, 0x02, 0x00, 0x78, 0x5F, 0xFF,
0xAD, 0x2A, 0xA4, 0x44, 0x14, 0x00, 0xA8, 0xF5, 0x55, 0x57, 0x95, 0x12,
0x11, 0x21, 0x00, 0xFC, 0xBF, 0xFE, 0xAB, 0xAA, 0x40, 0x88, 0x08, 0x00,
0x54, 0xD5, 0xAB, 0x56, 0x55, 0x8A, 0x22, 0x25, 0x00, 0xFE, 0xFF, 0xFD,
0xAB, 0x2A, 0x51, 0x48, 0x48, 0x00, 0xAA, 0xAA, 0x56, 0xD5, 0x56, 0x82,
0x12, 0x21, 0x00, 0xFE, 0xFF, 0xFF, 0xAB, 0xAA, 0x54, 0x44, 0x8A, 0x00,
0x55, 0x55, 0x55, 0x55, 0xB5, 0x80, 0x88, 0x10, 0x00, 0xFE, 0xFF, 0xFF,
0xAD, 0xAA, 0x2A, 0x52, 0xA4, 0x00, 0xAB, 0xAA, 0xAA, 0x55, 0x55, 0x44,
0x81, 0x12, 0x01, 0xFE, 0xFF, 0x7F, 0xAB, 0xAA, 0x22, 0x28, 0x44, 0x00,
0xAB, 0xAA, 0xEA, 0x6A, 0x55, 0x49, 0x45, 0x11, 0x01, 0xFD, 0xFF, 0xBF,
0xAB, 0xAA, 0x10, 0x20, 0x44, 0x00, 0x57, 0x55, 0xF5, 0xAA, 0x6D, 0xA5,
0x8A, 0x28, 0x01, 0xFA, 0xFF, 0xAF, 0x55, 0x95, 0x08, 0x21, 0x05, 0x00,
0xAE, 0xAA, 0x7E, 0xAB, 0x6A, 0x22, 0x4A, 0xA8, 0x00, 0xF6, 0xFF, 0xD5,
0x56, 0x55, 0x91, 0x10, 0x05, 0x00, 0x5A, 0x55, 0xFF, 0x55, 0x55, 0x44,
0x22, 0xA8, 0x00, 0xFC, 0xFF, 0x57, 0xAF, 0x2A, 0x29, 0x49, 0x05, 0x00,
0x54, 0x55, 0xFD, 0x6A, 0x55, 0x84, 0x04, 0x28, 0x00, 0xF8, 0xFF, 0x57,
0xAF, 0xAA, 0x22, 0x51, 0x05, 0x00, 0xA8, 0xAA, 0xFE, 0x55, 0x15, 0x88,
0x84, 0x10, 0x00, 0xF0, 0xFF, 0xAB, 0xBE, 0x96, 0x52, 0x28, 0x0A, 0x00,
0xA0, 0xAA, 0xFE, 0xAB, 0x4A, 0x04, 0x41, 0x00, 0x00, 0xC0, 0xFF, 0x57,
0xDD, 0x12, 0xA2, 0x94, 0x0A, 0x00, 0x80, 0xAA, 0xFE, 0x77, 0x45, 0x09,
0x21, 0x00, 0x00, 0x00, 0xFF, 0xAB, 0xBA, 0x21, 0x50, 0x8A, 0x02, 0x00,
0x00, 0xAA, 0xFE, 0x6F, 0x94, 0x82, 0x10, 0x00, 0x00, 0x00, 0xF8, 0xAB,
0x1A, 0x80, 0x28, 0x4A, 0x00, 0x00, 0x00, 0x40, 0xFF, 0x0B, 0x20, 0xA2,
0x00, 0x00, 0x00, 0x00, 0x80, 0xAA, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
};
#endif

View file

@ -0,0 +1,15 @@
#include "Arduino.h"
#include <U8g2lib.h>
#include "logos.h"
#include "utils.h"
#ifndef AFRAPAY_OLED_H
#define AFRAPAY_OLED_H
void drawLogo(U8G2 u8g2, e_logo logo);
void drawStatusText(U8G2 u8g2, String status, String statusRightAligned = "");
void updateOLED(U8G2 u8g2, e_state state, String statusText, String statusTextRightAligned = "");
void updateOLED(U8G2 u8g2, e_state state);
#endif

View file

@ -0,0 +1,95 @@
#ifndef AFRAPAY_PITCHES_H
#define AFRAPAY_PITCHES_H
#define NOTE_NONE 0
#define NOTE_B0 31
#define NOTE_C1 33
#define NOTE_CS1 35
#define NOTE_D1 37
#define NOTE_DS1 39
#define NOTE_E1 41
#define NOTE_F1 44
#define NOTE_FS1 46
#define NOTE_G1 49
#define NOTE_GS1 52
#define NOTE_A1 55
#define NOTE_AS1 58
#define NOTE_B1 62
#define NOTE_C2 65
#define NOTE_CS2 69
#define NOTE_D2 73
#define NOTE_DS2 78
#define NOTE_E2 82
#define NOTE_F2 87
#define NOTE_FS2 93
#define NOTE_G2 98
#define NOTE_GS2 104
#define NOTE_A2 110
#define NOTE_AS2 117
#define NOTE_B2 123
#define NOTE_C3 131
#define NOTE_CS3 139
#define NOTE_D3 147
#define NOTE_DS3 156
#define NOTE_E3 165
#define NOTE_F3 175
#define NOTE_FS3 185
#define NOTE_G3 196
#define NOTE_GS3 208
#define NOTE_A3 220
#define NOTE_AS3 233
#define NOTE_B3 247
#define NOTE_C4 262
#define NOTE_CS4 277
#define NOTE_D4 294
#define NOTE_DS4 311
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_FS4 370
#define NOTE_G4 392
#define NOTE_GS4 415
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
#define NOTE_D5 587
#define NOTE_DS5 622
#define NOTE_E5 659
#define NOTE_F5 698
#define NOTE_FS5 740
#define NOTE_G5 784
#define NOTE_GS5 831
#define NOTE_A5 880
#define NOTE_AS5 932
#define NOTE_B5 988
#define NOTE_C6 1047
#define NOTE_CS6 1109
#define NOTE_D6 1175
#define NOTE_DS6 1245
#define NOTE_E6 1319
#define NOTE_F6 1397
#define NOTE_FS6 1480
#define NOTE_G6 1568
#define NOTE_GS6 1661
#define NOTE_A6 1760
#define NOTE_AS6 1865
#define NOTE_B6 1976
#define NOTE_C7 2093
#define NOTE_CS7 2217
#define NOTE_D7 2349
#define NOTE_DS7 2489
#define NOTE_E7 2637
#define NOTE_F7 2794
#define NOTE_FS7 2960
#define NOTE_G7 3136
#define NOTE_GS7 3322
#define NOTE_A7 3520
#define NOTE_AS7 3729
#define NOTE_B7 3951
#define NOTE_C8 4186
#define NOTE_CS8 4435
#define NOTE_D8 4699
#define NOTE_DS8 4978
#endif

View file

@ -0,0 +1,26 @@
#include <Arduino.h>
#include <HTTPClient.h>
#ifndef AFRAPAY_UTILS_H
#define AFRAPAY_UTILS_H
unsigned long cooldownSecondsRemaining(unsigned long timeout, unsigned long timer);
String byteArrayAsHexString(byte *buffer, byte bufferSize);
String cardLink(WiFiClient* wifi, HTTPClient* http, String apiUrl, String cardId);
String cardBalance(WiFiClient* wifi, HTTPClient* http, String apiUrl, String cardId);
String cardTransaction(WiFiClient* wifi, HTTPClient* http, String apiUrl, String cardId, String amount);
String splitString(String data, char separator, int index);
enum e_state {
STATE_IDLE,
STATE_TRANSACT_CARDSCAN,
STATE_TRANSACT_VERIFY,
STATE_LINK_CARDSCAN,
STATE_LINK_VERIFY,
STATE_BALANCE_CARDSCAN,
STATE_BALANCE_VERIFY,
STATE_RESULT_SUCCESS,
STATE_RESULT_FAILURE,
STATE_RESULT_DISPLAY,
};
#endif

View file

@ -0,0 +1,9 @@
#ifndef AFRAPAY_WIFIFIX_H
#define AFRAPAY_WIFIFIX_H
class WiFiClientFixed : public WiFiClient {
public:
void flush() override;
};
#endif

View file

@ -0,0 +1,22 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:az-delivery-devkit-v4]
platform = espressif32@^6.0.0
board = az-delivery-devkit-v4
framework = arduino
upload_speed = 921600
monitor_speed = 115200
lib_deps =
olikraus/U8g2@^2.34.13
juerd/ESP-WiFiSettings@^3.8.0
miguelbalboa/MFRC522@^1.4.10
arduino-libraries/NTPClient@^3.2.1
mcxiaoke/ESPDateTime@^1.0.4

View file

@ -0,0 +1,213 @@
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <SPIFFS.h>
#include <WiFiSettings.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <SPI.h>
#include <MFRC522.h>
#include <ESPDateTime.h>
#include <DateTimeTZ.h>
#include "wifiFix.h"
#include "pitches.h"
#include "utils.h"
#include "oled.h"
#define HSPI_MISO 12
#define HSPI_MOSI 13
#define HSPI_SCLK 14
#define HSPI_SS 15
#define HSPI_RST 16
#define BUZZER_PIN 26
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
MFRC522 mfrc522(HSPI_SS, HSPI_RST);
WiFiClient *wifi = new WiFiClientFixed();
HTTPClient *http = new HTTPClient();
e_state state = STATE_IDLE;
String apiUrl = "";
const int gTimeout = 5000;
const int scanTimeout = 15000;
String scannedCardId = "";
String lastStatusText = "";
unsigned long timer = 0;
void IRAM_ATTR TransactInterruptHandler() {
if (state != STATE_IDLE) {
return;
}
timer = millis();
state = STATE_TRANSACT_CARDSCAN;
}
void IRAM_ATTR LinkInterruptHandler() {
if (state != STATE_IDLE) {
return;
}
timer = millis();
state = STATE_LINK_CARDSCAN;
}
void IRAM_ATTR BalanceInterruptHandler() {
if (state != STATE_IDLE) {
return;
}
timer = millis();
state = STATE_BALANCE_CARDSCAN;
}
bool cooldownCheck(long timeout) {
if (millis() - timer > timeout) {
state = STATE_IDLE;
updateOLED(u8g2, state);
return true;
}
return false;
}
void setup() {
Serial.begin(115200);
u8g2.begin();
drawLogo(u8g2, LOGO_MATECARD);
u8g2.sendBuffer();
SPI.begin(HSPI_SCLK, HSPI_MISO, HSPI_MOSI);
SPIFFS.begin(true);
WiFiSettings.hostname = "afrapay-";
apiUrl = WiFiSettings.string("AfRApay.Web API", "http://192.168.50.170:5296");
WiFiSettings.connect();
mfrc522.PCD_Init();
DateTime.setTimeZone(TZ_Europe_Berlin);
DateTime.begin();
updateOLED(u8g2, state);
ledcSetup(0, 5000, 12);
pinMode(25, INPUT_PULLUP);
pinMode(32, INPUT_PULLUP);
pinMode(33, INPUT_PULLUP);
attachInterrupt(25, BalanceInterruptHandler, FALLING);
attachInterrupt(32, LinkInterruptHandler, FALLING);
attachInterrupt(33, TransactInterruptHandler, FALLING);
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
updateOLED(u8g2, state, "WiFi disconnected :(");
state = STATE_IDLE;
WiFi.reconnect();
delay(50);
return;
}
switch (state) {
case STATE_IDLE:
updateOLED(u8g2, state);
break;
case STATE_TRANSACT_CARDSCAN:
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
scannedCardId = byteArrayAsHexString(mfrc522.uid.uidByte, mfrc522.uid.size);
mfrc522.PICC_HaltA();
lastStatusText = "Card #" + scannedCardId;
state = STATE_TRANSACT_VERIFY;
return;
} else {
if (!cooldownCheck(scanTimeout)) {
updateOLED(u8g2, state, String("1.50€"), String(cooldownSecondsRemaining(scanTimeout, timer)));
}
}
break;
case STATE_TRANSACT_VERIFY:
updateOLED(u8g2, state, lastStatusText);
tone(BUZZER_PIN, NOTE_A5, 25);
tone(BUZZER_PIN, NOTE_NONE, 150);
lastStatusText = cardTransaction(wifi, http, apiUrl, scannedCardId, "1.50");
if (lastStatusText.startsWith("S:")) {
tone(BUZZER_PIN, NOTE_C7, 650);
state = STATE_RESULT_SUCCESS;
} else {
tone(BUZZER_PIN, NOTE_CS5, 100);
tone(BUZZER_PIN, NOTE_NONE, 25);
tone(BUZZER_PIN, NOTE_CS5, 400);
state = STATE_RESULT_FAILURE;
}
lastStatusText = lastStatusText.substring(2);
timer = millis();
return;
case STATE_LINK_CARDSCAN:
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
scannedCardId = byteArrayAsHexString(mfrc522.uid.uidByte, mfrc522.uid.size);
mfrc522.PICC_HaltA();
lastStatusText = "Card #" + scannedCardId;
state = STATE_LINK_VERIFY;
return;
} else {
if (!cooldownCheck(scanTimeout)) {
updateOLED(u8g2, state, "Link", String(cooldownSecondsRemaining(scanTimeout, timer)));
}
}
break;
case STATE_LINK_VERIFY:
updateOLED(u8g2, state, lastStatusText);
tone(BUZZER_PIN, NOTE_A5, 25);
tone(BUZZER_PIN, NOTE_NONE, 150);
lastStatusText = cardLink(wifi, http, apiUrl, scannedCardId);
if (lastStatusText.startsWith("S:")) {
tone(BUZZER_PIN, NOTE_C6, 100);
tone(BUZZER_PIN, NOTE_NONE, 10);
tone(BUZZER_PIN, NOTE_C6, 100);
state = STATE_RESULT_SUCCESS;
} else {
tone(BUZZER_PIN, NOTE_CS5, 100);
tone(BUZZER_PIN, NOTE_NONE, 25);
tone(BUZZER_PIN, NOTE_CS5, 400);
state = STATE_RESULT_FAILURE;
}
lastStatusText = lastStatusText.substring(2);
timer = millis();
return;
case STATE_BALANCE_CARDSCAN:
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
scannedCardId = byteArrayAsHexString(mfrc522.uid.uidByte, mfrc522.uid.size);
mfrc522.PICC_HaltA();
lastStatusText = "Card #" + scannedCardId;
state = STATE_BALANCE_VERIFY;
return;
} else {
if (!cooldownCheck(scanTimeout)) {
updateOLED(u8g2, state, "Balance", String(cooldownSecondsRemaining(scanTimeout, timer)));
}
}
break;
case STATE_BALANCE_VERIFY:
updateOLED(u8g2, state, lastStatusText);
tone(BUZZER_PIN, NOTE_A5, 25);
tone(BUZZER_PIN, NOTE_NONE, 150);
lastStatusText = cardBalance(wifi, http, apiUrl, scannedCardId);
if (lastStatusText.startsWith("S:")) {
lastStatusText = lastStatusText + ":Card #" + scannedCardId;
state = STATE_RESULT_DISPLAY;
} else {
tone(BUZZER_PIN, NOTE_CS5, 100);
tone(BUZZER_PIN, NOTE_NONE, 25);
tone(BUZZER_PIN, NOTE_CS5, 400);
state = STATE_RESULT_FAILURE;
}
lastStatusText = lastStatusText.substring(2);
timer = millis();
case STATE_RESULT_SUCCESS:
case STATE_RESULT_FAILURE:
case STATE_RESULT_DISPLAY:
if (!cooldownCheck(gTimeout)) {
updateOLED(u8g2, state, lastStatusText, String(cooldownSecondsRemaining(gTimeout, timer)));
}
break;
}
delay(50);
}

View file

@ -0,0 +1,130 @@
#include <WiFi.h>
#include <ESPDateTime.h>
#include "Arduino.h"
#include "U8g2lib.h"
#include "logos.h"
#include "utils.h"
void drawCurvedLineV(U8G2 u8g2, int xStart, int yStart, int vert, int horiz) {
u8g2.drawVLine(xStart, yStart + 1, vert);
u8g2.drawVLine(xStart + 1, yStart, vert);
u8g2.drawHLine(xStart, yStart + vert, horiz);
u8g2.drawHLine(xStart + 1, yStart + vert + 1, horiz);
}
void drawCurvedLine(U8G2 u8g2, int xStart, int yStart, int horiz, int vert) {
u8g2.drawHLine(xStart, yStart, horiz);
u8g2.drawHLine(xStart, yStart + 1, horiz);
u8g2.drawVLine(xStart + horiz, yStart, vert);
u8g2.drawVLine(xStart + horiz + 1, yStart + 1, vert);
}
void drawLogoAfraPay(U8G2 u8g2) {
u8g2.setFontMode(1);
u8g2.setFontDirection(0);
int xOffset = 12;
int yOffset = 23;
u8g2.setFont(u8g2_font_maniac_tr);
u8g2.drawStr(xOffset, yOffset, "AfRA");
u8g2.setFont(u8g2_font_tenthinguys_tr);
u8g2.drawStr(xOffset + 73, yOffset + 3, "pay");
// uncomment to verify distance to screen gap is equal on both sides
//u8g2.drawHLine(0, yOffset-12, xOffset);
//u8g2.drawHLine(127-xOffset-1, yOffset, xOffset);
drawCurvedLineV(u8g2, xOffset + 69, yOffset, 7, 32);
drawCurvedLine(u8g2, xOffset + 73, yOffset - 8, 28, 15);
u8g2.drawPixel(xOffset + 101, yOffset + 7);
}
void drawLogoBitmap(U8G2 u8g2, int width, int height, unsigned char bits[], int yOffset = 0) {
int offset = (128 - width) / 2;
u8g2.drawXBM(offset, yOffset, width, height, bits);
}
void drawLogo(U8G2 u8g2, e_logo logo) {
switch (logo) {
case LOGO_AFRAPAY:
drawLogoAfraPay(u8g2);
break;
case LOGO_CONTACTLESS:
drawLogoBitmap(u8g2, contactless_width, contactless_height, contactless_bits);
break;
case LOGO_MATECARD:
drawLogoBitmap(u8g2, matecard_width, matecard_height, matecard_bits, 5);
u8g2.setFont(u8g2_font_bpixeldouble_tr);
u8g2.drawStr((126 - u8g2.getStrWidth("matecard")) / 2, 57, "matecard");
break;
case LOGO_PENDING:
drawLogoBitmap(u8g2, pending_width, pending_height, pending_bits, 5);
break;
case LOGO_SUCCESS:
drawLogoBitmap(u8g2, success_width, success_height, success_bits, 5);
break;
case LOGO_FAILURE:
drawLogoBitmap(u8g2, failure_width, failure_height, failure_bits, 5);
break;
}
}
void drawStatusText(U8G2 u8g2, String status, String statusRightAligned = "") {
u8g2.setFont(u8g2_font_bpixel_te);
u8g2.drawUTF8(0, 61, status.c_str());
if (!statusRightAligned.isEmpty()) {
int textWidth = u8g2.getUTF8Width(statusRightAligned.c_str());
u8g2.drawUTF8(126 - textWidth, 61, statusRightAligned.c_str());
}
}
void drawFullScreenText(U8G2 u8g2, String textTop, String textBottom) {
u8g2.setFont(u8g2_font_chargen_92_tr);
u8g2.drawUTF8((127-u8g2.getUTF8Width(textTop.c_str()))/2, 13, textTop.c_str());
u8g2.setFont(u8g2_font_maniac_te);
u8g2.drawUTF8((127-u8g2.getUTF8Width(textBottom.c_str()))/2, 45, textBottom.c_str());
}
void updateOLED(U8G2 u8g2, e_state state, String statusText, String statusTextRightAligned = "") {
u8g2.clearBuffer();
switch (state) {
case STATE_IDLE:
drawLogo(u8g2, LOGO_AFRAPAY);
break;
case STATE_TRANSACT_CARDSCAN:
case STATE_BALANCE_CARDSCAN:
case STATE_LINK_CARDSCAN:
drawLogo(u8g2, LOGO_CONTACTLESS);
break;
case STATE_RESULT_SUCCESS:
drawLogo(u8g2, LOGO_SUCCESS);
break;
case STATE_RESULT_FAILURE:
drawLogo(u8g2, LOGO_FAILURE);
break;
case STATE_RESULT_DISPLAY:
drawFullScreenText(u8g2, splitString(statusText, ':', 0), splitString(statusText, ':', 1) + "");
drawStatusText(u8g2, splitString(statusText, ':', 2), statusTextRightAligned);
u8g2.sendBuffer();
return;
case STATE_TRANSACT_VERIFY:
case STATE_BALANCE_VERIFY:
case STATE_LINK_VERIFY:
drawLogo(u8g2, LOGO_PENDING);
break;
}
drawStatusText(u8g2, statusText, statusTextRightAligned);
u8g2.sendBuffer();
}
void updateOLED(U8G2 u8g2, e_state state) {
String time = DateTime.format(DateFormatter::TIME_ONLY);
updateOLED(u8g2, state, time, WiFi.localIP().toString());
}

View file

@ -0,0 +1,80 @@
#include <Arduino.h>
#include <WiFiClient.h>
#include <HTTPClient.h>
String byteArrayAsHexString(byte *buffer, byte bufferSize) {
String s = "";
for (byte i = 0; i < bufferSize; i++) {
s += (buffer[i] < 0x10 ? "0" : "");
s += String(buffer[i], HEX);
}
return s;
}
unsigned long cooldownSecondsRemaining(unsigned long timeout, unsigned long timer) {
return (timeout - (millis() - timer)) / 1000 + 1;
}
String splitString(String data, char separator, int index) {
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = data.length() - 1;
for (int i = 0; i <= maxIndex && found <= index; i++) {
if (data.charAt(i) == separator || i == maxIndex) {
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i + 1 : i;
}
}
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
String cardLink(WiFiClient *wifi, HTTPClient *http, String apiUrl, String cardId) {
String finalRequestUrl = apiUrl + "/api/card/link?card=" + cardId;
http->begin(*wifi, finalRequestUrl.c_str());
int httpResponseCode = http->GET();
if (httpResponseCode == 200) {
String payload = http->getString();
http->end();
return payload;
}
http->end();
if (httpResponseCode > 0) {
return String("E:HTTP Error ") + httpResponseCode;
}
return String("E:Internal Error ") + httpResponseCode;
}
String cardBalance(WiFiClient *wifi, HTTPClient *http, String apiUrl, String cardId) {
String finalRequestUrl = apiUrl + "/api/card/balance?card=" + cardId;
http->begin(*wifi, finalRequestUrl.c_str());
int httpResponseCode = http->GET();
if (httpResponseCode == 200) {
String payload = http->getString();
http->end();
return payload;
}
http->end();
if (httpResponseCode > 0) {
return String("E:HTTP Error ") + httpResponseCode;
}
return String("E:Internal Error ") + httpResponseCode;
}
String cardTransaction(WiFiClient *wifi, HTTPClient *http, String apiUrl, String cardId, String amount) {
String finalRequestUrl = apiUrl + "/api/card/transaction?card=" + cardId + "&amount=" + amount;
http->begin(*wifi, finalRequestUrl.c_str());
int httpResponseCode = http->GET();
if (httpResponseCode == 200) {
String payload = http->getString();
http->end();
return payload;
}
http->end();
if (httpResponseCode > 0) {
return String("E:HTTP Error ") + httpResponseCode;
}
return String("E:Internal Error ") + httpResponseCode;
}

View file

@ -0,0 +1,34 @@
#include <WiFi.h>
#include <lwip/sockets.h>
#define WIFI_CLIENT_FLUSH_BUFFER_SIZE (1024)
class WiFiClientFixed : public WiFiClient {
public:
void flush() override;
};
void WiFiClientFixed::flush() {
int res;
size_t a = available(), toRead = 0;
if (!a) {
return;//nothing to flush
}
uint8_t *buf = (uint8_t *) malloc(WIFI_CLIENT_FLUSH_BUFFER_SIZE);
if (!buf) {
return;//memory error
}
while (a) {
toRead = (a > WIFI_CLIENT_FLUSH_BUFFER_SIZE) ? WIFI_CLIENT_FLUSH_BUFFER_SIZE : a;
// override broken WiFiClient flush method, ref https://github.com/espressif/arduino-esp32/issues/6129#issuecomment-1237417915
//res = recv(fd(), buf, toRead, MSG_DONTWAIT);
res = read(buf, a);
if (res < 0) {
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
stop();
break;
}
a -= res;
}
free(buf);
}

View file

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="linq2db" Version="4.4.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="7.0.2" />
<PackageReference Include="System.Data.SQLite" Version="1.0.115" />
<PackageReference Include="System.Data.SQLite.Core.osx.arm64" Version="1.0.117" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,34 @@
using AfRApay.Web.Backend.Tables;
using LinqToDB;
using LinqToDB.Configuration;
using LinqToDB.Data;
namespace AfRApay.Web.Backend;
public class Database {
private class ConnectionStringSettings : IConnectionStringSettings {
public string ConnectionString { get; set; } = null!;
public string Name { get; set; } = null!;
public string ProviderName { get; set; } = null!;
public bool IsGlobal => false;
}
public class Settings : ILinqToDBSettings {
public IEnumerable<IDataProviderSettings> DataProviders => Enumerable.Empty<IDataProviderSettings>();
public string DefaultConfiguration => "SQLite";
public string DefaultDataProvider => "SQLite";
public IEnumerable<IConnectionStringSettings> ConnectionStrings {
get { yield return new ConnectionStringSettings { Name = "db", ProviderName = "SQLite", ConnectionString = @"Data Source=database.db;" }; }
}
}
public class DbConn : DataConnection {
public DbConn() : base("db") { }
public ITable<DbInfo> DbInfo => this.GetTable<DbInfo>();
public ITable<User> Users => this.GetTable<User>();
public ITable<Card> Cards => this.GetTable<Card>();
public ITable<Config> Config => this.GetTable<Config>();
}
}

View file

@ -0,0 +1,91 @@
using AfRApay.Web.Backend.Tables;
using LinqToDB;
using LinqToDB.Data;
namespace AfRApay.Web.Backend;
public static class Migrations {
private const int DbVer = 1;
// ReSharper disable once CollectionNeverUpdated.Local
// ReSharper disable once InconsistentNaming
private static readonly List<Migration> _migrations = new();
public static void RunMigrations() {
using var db = new Database.DbConn();
var ccolor = Console.ForegroundColor;
if (!db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.Any()) {
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.Write("Running migration: ");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Initialize Database");
db.CreateTable<DbInfo>();
db.CreateTable<User>();
db.CreateTable<Card>();
db.CreateTable<Config>();
db.InsertWithIdentity(new DbInfo { DbVer = DbVer });
}
else if (db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.All(t => t.TableName != "DbInfo")) {
db.CreateTable<DbInfo>();
db.InsertWithIdentity(new DbInfo { DbVer = 0 });
}
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"Database version: {db.DbInfo.ToList().First().DbVer}");
var migrationsToRun = _migrations.FindAll(p => p.IntroducedWithDbVer > db.DbInfo.First().DbVer);
if (migrationsToRun.Count == 0) {
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("No migrations to run.");
}
else {
new Migration(0, "BEGIN TRANSACTION").Run(db);
try {
migrationsToRun.ForEach(p => p.Run(db));
}
catch {
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine($"Migrating to database version {DbVer} failed.");
new Migration(0, "ROLLBACK TRANSACTION").Run(db);
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("Rolled back migrations.");
Environment.Exit(1);
}
new Migration(0, "COMMIT TRANSACTION").Run(db);
var newdb = new Database.DbConn();
var dbinfo = newdb.DbInfo.First();
dbinfo.DbVer = DbVer;
newdb.Update(dbinfo);
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine($"Database version is now: {DbVer}");
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Finished running migrations.");
}
Console.ForegroundColor = ccolor;
}
private class Migration {
private readonly string _sql;
public readonly int IntroducedWithDbVer;
public Migration(int introducedWithDbVer, string sql) {
IntroducedWithDbVer = introducedWithDbVer;
_sql = sql;
}
public void Run(DataConnection db) {
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.Write("Running migration: ");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(_sql);
db.Execute(_sql);
}
}
}

View file

@ -0,0 +1,37 @@
using AfRApay.Web.Backend;
using LinqToDB.Data;
DataConnection.DefaultSettings = new Database.Settings();
Migrations.RunMigrations();
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
#if (DEBUG)
builder.Services.AddControllers().AddRazorRuntimeCompilation();
builder.Services.AddControllers();
#else
builder.Services.AddControllers();
#endif
var app = builder.Build();
if (!app.Environment.IsDevelopment()) {
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/error");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
app.Run();

View file

@ -0,0 +1,11 @@
using LinqToDB.Mapping;
#pragma warning disable CS8618
namespace AfRApay.Web.Backend.Tables;
[Table(Name = "Cards")]
public class Card {
[Column(Name = "CardId"), PrimaryKey, NotNull] public string CardId { get; set; }
[Column(Name = "UserId"), NotNull] public int UserId { get; set; }
}

View file

@ -0,0 +1,9 @@
using LinqToDB.Mapping;
namespace AfRApay.Web.Backend.Tables;
[Table(Name = "Config")]
public class Config {
[Column(Name = "Name"), PrimaryKey, NotNull] public string Name { get; set; }
[Column(Name = "Value"), NotNull] public string Value { get; set; }
}

View file

@ -0,0 +1,9 @@
using LinqToDB.Mapping;
namespace AfRApay.Web.Backend.Tables;
[Table(Name = "DbInfo")]
public class DbInfo {
[Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; }
[Column(Name = "DbVer"), NotNull] public int DbVer { get; set; }
}

View file

@ -0,0 +1,12 @@
using LinqToDB.Mapping;
#pragma warning disable CS8618
namespace AfRApay.Web.Backend.Tables;
[Table(Name = "Users")]
public class User {
[Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; }
[Column(Name = "Nickname"), NotNull] public string Nickname { get; set; }
[Column(Name = "Balance")] public decimal Balance { get; set; }
}

View file

@ -0,0 +1,21 @@
using AfRApay.Web.Backend;
using AfRApay.Web.Backend.Tables;
using LinqToDB;
using Microsoft.AspNetCore.Mvc;
namespace AfRApay.Web.Controllers;
[ApiController, Route("/api/card/balance")]
public class CardBalance : Controller {
[HttpGet]
public string Get([FromQuery] string card) {
var db = new Database.DbConn();
if (db.Cards.Any(p => p.CardId == card)) {
var userId = db.Cards.First(p => p.CardId == card).UserId;
var user = db.Users.First(p => p.Id == userId);
return $"S:{user.Nickname}:{user.Balance:N2}";
}
return "E:Unknown card.";
}
}

View file

@ -0,0 +1,40 @@
using AfRApay.Web.Backend;
using AfRApay.Web.Backend.Tables;
using LinqToDB;
using Microsoft.AspNetCore.Mvc;
namespace AfRApay.Web.Controllers;
[ApiController, Route("/api/card/link")]
public class CardLink : Controller {
[HttpGet]
public string Get([FromQuery] string card) {
var db = new Database.DbConn();
if (db.Cards.Any(p => p.CardId == card)) {
return "E:Already registered.";
}
var linkFlag = db.Config.FirstOrDefault(p => p.Name == "link");
if (string.IsNullOrWhiteSpace(linkFlag?.Value)) {
return "E:No link flag set.";
}
var lTimeFlag = db.Config.FirstOrDefault(p => p.Name == "lTime");
if (string.IsNullOrWhiteSpace(lTimeFlag?.Value)) {
return "E:No link flag set.";
}
if (DateTime.UtcNow - DateTime.Parse(lTimeFlag.Value) > TimeSpan.FromMinutes(5)) {
return "E:Link expired, try again.";
}
var user = db.Users.First(p => p.Id == int.Parse(linkFlag.Value));
linkFlag.Value = "";
db.Insert(new Card { CardId = card, UserId = user.Id });
db.Update(linkFlag);
return $"S:Reg. -> {user.Nickname}";
}
}

View file

@ -0,0 +1,27 @@
using AfRApay.Web.Backend;
using LinqToDB;
using Microsoft.AspNetCore.Mvc;
namespace AfRApay.Web.Controllers;
[ApiController, Route("/api/card/transaction")]
public class CardTransaction : Controller {
[HttpGet]
public string Get([FromQuery] string card, [FromQuery] decimal amount) {
var db = new Database.DbConn();
if (db.Cards.Any(p => p.CardId == card)) {
var userId = db.Cards.First(p => p.CardId == card).UserId;
var user = db.Users.First(p => p.Id == userId);
if (user.Balance - amount < -50) {
return "E:Balance too low!";
}
user.Balance -= amount;
db.Update(user);
return $"S:{user.Nickname} - {user.Balance:N2}";
}
return "E:Unknown card.";
}
}

View file

@ -0,0 +1,19 @@
@page
@model AddUserModel
@{
ViewData["Title"] = "Add User";
}
<div class="text-center">
<h1 class="display-5">
Add User
</h1>
</div>
<form method="POST">
<div class="mb-3">
<label for="nickname" class="form-label">Nickname</label>
<input type="text" maxlength="10" class="form-control" id="nickname" name="nickname" placeholder="nick" required>
</div>
<button type="submit" class="btn btn-primary" name="action" value="save">Save</button>
</form>

View file

@ -0,0 +1,28 @@
using System.Data;
using AfRApay.Web.Backend;
using AfRApay.Web.Backend.Tables;
using LinqToDB;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AfRApay.Web.Pages;
public class AddUserModel : PageModel {
public void OnGet() { }
public void OnPost() {
using var db = new Database.DbConn();
if (Request.Form.ContainsKey("nickname") && !string.IsNullOrWhiteSpace(Request.Form["nickname"])) {
var nick = Request.Form["nickname"];
if (db.Users.Any(p => p.Nickname == nick)) {
throw new DuplicateNameException("User with nick already exists.");
}
var user = new User { Nickname = nick!, Balance = 0};
db.InsertWithIdentity(user);
Response.Redirect($"/#{user.Nickname}");
return;
}
throw new ConstraintException("Nickname must not be empty.");
}
}

View file

@ -0,0 +1,69 @@
@page "{id:int}"
@using AfRApay.Web.Backend
@model EditUserModel
@{
ViewData["Title"] = "Edit User";
if (Request.Method == "POST" && Request.Form["action"] == "delete") {
return;
}
var db = new Database.DbConn();
var user = db.Users.First(p => p.Id == int.Parse(RouteData.Values["id"]!.ToString()!));
var cards = db.Cards.Where(p => p.UserId == user.Id);
}
<div class="text-center">
<h1 class="display-5">
Edit User
</h1>
</div>
<form method="POST">
<h3>Account</h3>
<div class="mb-3">
<label for="nickname" class="form-label">Nickname</label>
<input type="text" maxlength="10" class="form-control" id="nickname" name="nickname" value="@user.Nickname" required>
</div>
<button type="submit" class="btn btn-primary" name="action" value="save">Save</button>
<button type="submit" class="btn btn-danger" name="action" value="delete">Delete</button>
</form>
<br/>
<h3>Cards</h3>
@if (!cards.Any()) {
<div>
No linked cards found.
</div>
}
else {
<div class="row">
@foreach (var card in cards) {
<div class="fake-card-wrapper">
<div class="fake-card">
<div class="face-card front">
<div class="chip">
<div>
<div>
<div>
</div>
</div>
</div>
</div>
<h2 class="card-number">@card.CardId</h2>
<h3 class="card-holder">@user.Nickname</h3>
<span class="validity">
<span class="small">VALID<br>THRU</span>
<span>01/2038</span>
</span>
<div class="fake-card-logo-top">
<img src="/img/afra.png" class="fake-card-logo-inner" alt="">
</div>
<div class="fake-card-logo-bottom">
<img src="/img/matecard.png" class="fake-card-logo-inner" alt="">
</div>
</div>
</div>
</div>
}
</div>
}

View file

@ -0,0 +1,33 @@
using System.Data;
using AfRApay.Web.Backend;
using LinqToDB;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AfRApay.Web.Pages;
public class EditUserModel : PageModel {
public void OnGet() { }
public void OnPost() {
using var db = new Database.DbConn();
var userId = int.Parse(RouteData.Values["id"]!.ToString()!);
var user = db.Users.First(p => p.Id == userId);
if (Request.Form["action"] == "delete") {
db.Delete(user);
Response.Redirect("/");
return;
}
if (Request.Form.ContainsKey("nickname") && !string.IsNullOrWhiteSpace(Request.Form["nickname"])) {
var nick = Request.Form["nickname"];
if (db.Users.Any(p => p.Nickname == nick)) {
throw new DuplicateNameException("User with nick already exists.");
}
user.Nickname = nick!;
db.Update(user);
}
Response.Redirect($"/#{user.Nickname}");
}
}

View file

@ -0,0 +1,27 @@
@page
@using Microsoft.AspNetCore.WebUtilities
@model ErrorModel
@{
ViewData["Title"] = $"error_{Response.StatusCode}";
}
@if (Response.StatusCode == 200) {
<h1>No error occured.</h1>
<p>Why are you here?</p>
}
else {
<h1>Error @Response.StatusCode: @ReasonPhrases.GetReasonPhrase(Response.StatusCode)</h1>
if (Response.StatusCode == 404) {
<p>The page you requested does not exist on this server.</p>
}
else {
<p>An error occurred while processing your request.</p>
}
}
@if (Model.ShowRequestId) {
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}

View file

@ -0,0 +1,20 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AfRApay.Web.Pages;
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true), IgnoreAntiforgeryToken]
public class ErrorModel : PageModel {
private readonly ILogger<ErrorModel> _logger;
public ErrorModel(ILogger<ErrorModel> logger) => _logger = logger;
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet() {
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}

View file

@ -0,0 +1,76 @@
@page
@using AfRApay.Web.Backend
@model IndexModel
@{
ViewData["Title"] = "Index";
var db = new Database.DbConn();
}
@section Stylesheets {
<link rel="stylesheet" href="~/lib/datatables/dataTables.bootstrap5.min.css"/>
}
@section Scripts {
<script src="~/lib/datatables/jquery.dataTables.min.js"></script>
<script src="~/lib/datatables/dataTables.bootstrap5.min.js"></script>
<script>
$(document).ready( function () {
let search = "";
if (window.location.hash.startsWith("#")) {
search = decodeURI(window.location.hash.substring(1));
}
$('#users').DataTable({
lengthMenu: [10, 25, 50, 100, -1],
pageLength: 25,
search: {
search: search,
}
});
} );
</script>
}
<div class="text-center">
<h1 class="display-5">
Users
<a class="btn btn-lg btn-primary" href="/AddUser">Add</a>
</h1>
</div>
<div>
<table class="table table-striped table-hover" id="users">
<thead>
<tr>
<th scope="col">Nickame</th>
<th scope="col">Balance</th>
<th scope="col">Cards</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var user in db.Users.OrderBy(p => p.Nickname.ToLower())) {
<tr id="user_@user.Id">
<td>
<b>@user.Nickname</b>
</td>
<td>
@($"{user.Balance:C}")
</td>
<td>
<b>@db.Cards.Count(p => p.UserId == user.Id)</b> cards linked.
</td>
<td>
<div class="btn-group" role="group">
<a class="btn btn-sm btn-danger" href="/UpdateBalance/@user.Id/-1.50">-1.50&euro;</a>
<a class="btn btn-sm btn-success" href="/UpdateBalance/@user.Id/5">+5&euro;</a>
<a class="btn btn-sm btn-success" href="/UpdateBalance/@user.Id/10">+10&euro;</a>
<a class="btn btn-sm btn-secondary" href="/LinkCard/@user.Id">Link card</a>
<a class="btn btn-sm btn-primary" href="/EditUser/@user.Id">Edit</a>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>

View file

@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AfRApay.Web.Pages;
public class IndexModel : PageModel {
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger) {
_logger = logger;
}
public void OnGet() { }
}

View file

@ -0,0 +1,15 @@
@page "{id:int}"
@using AfRApay.Web.Backend
@model LinkCardModel
@{
ViewData["Title"] = "Link Card";
}
<div class="text-center">
<h1 class="display-5">
Link card
</h1>
<p>
Link process initiated. Please go to the MateCard terminal and press the <b>[LINK]</b> button, then swipe your card or token within the next 5 minutes. Click <a href="/"><b>here</b></a> when you are done with the process.
</p>
</div>

View file

@ -0,0 +1,43 @@
using System.Data;
using System.Security.Authentication;
using AfRApay.Web.Backend;
using AfRApay.Web.Backend.Tables;
using LinqToDB;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AfRApay.Web.Pages;
public class LinkCardModel : PageModel {
public void OnGet(int id) {
using var db = new Database.DbConn();
var linkFlag = db.Config.FirstOrDefault(p => p.Name == "link");
var lTimeFlag = db.Config.FirstOrDefault(p => p.Name == "lTime");
var user = db.Users.FirstOrDefault(p => p.Id == id);
if (user == null) {
throw new InvalidCredentialException("User does not exist");
}
if (lTimeFlag == null) {
lTimeFlag = new Config { Name = "lTime", Value = DateTime.UtcNow.ToString("s") };
db.Insert(lTimeFlag);
}
else {
lTimeFlag.Value = DateTime.UtcNow.ToString("s");
db.Update(lTimeFlag);
}
if (linkFlag == null) {
linkFlag = new Config { Name = "link", Value = id.ToString() };
db.Insert(linkFlag);
return;
}
if (linkFlag.Value.Equals(id.ToString())) {
return;
}
linkFlag.Value = id.ToString();
db.Update(linkFlag);
}
}

View file

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>@ViewData["Title"] - AfRApay.Web</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css"/>
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true"/>
@await RenderSectionAsync("Stylesheets", false)
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">AfRApay.Web</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
&copy; @DateTime.Now.Year - AfRApay team
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View file

@ -0,0 +1,48 @@
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
for details on configuring this project to bundle and minify static web assets. */
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
a {
color: #0077cc;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.border-top {
border-top: 1px solid #e5e5e5;
}
.border-bottom {
border-bottom: 1px solid #e5e5e5;
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
button.accept-policy {
font-size: 1rem;
line-height: inherit;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
white-space: nowrap;
line-height: 60px;
}

View file

@ -0,0 +1,2 @@
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

View file

@ -0,0 +1,5 @@
@page "{id:int}/{amount:decimal}"
@model UpdateBalanceModel
@{
Layout = null;
}

View file

@ -0,0 +1,26 @@
using System.Data;
using AfRApay.Web.Backend;
using LinqToDB;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AfRApay.Web.Pages;
public class UpdateBalanceModel : PageModel {
public void OnGet(int id, decimal amount) {
var db = new Database.DbConn();
var user = db.Users.FirstOrDefault(p => p.Id == id);
if (user == null) {
Response.Redirect("/Error");
return;
}
switch (user.Balance + amount) {
case < -50: throw new ConstraintException("Balance too low!");
case > 999: throw new ConstraintException("Balance too high!");
}
user.Balance += amount;
db.Update(user);
Response.Redirect($"/#{user.Nickname}");
}
}

View file

@ -0,0 +1,3 @@
@using AfRApay.Web
@namespace AfRApay.Web.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View file

@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}

View file

@ -0,0 +1,22 @@
{
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://0.0.0.0:5296",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://0.0.0.0:7115;http://localhost:5296",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View file

@ -0,0 +1,9 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View file

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View file

@ -0,0 +1,256 @@
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
html {
position: relative;
min-height: 100%;
}
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
for details on configuring this project to bundle and minify static web assets. */
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
a {
color: #0077cc;
}
td {
vertical-align: middle !important;
text-align: center;
}
th {
text-align: center !important;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.border-top {
border-top: 1px solid #e5e5e5;
}
.border-bottom {
border-bottom: 1px solid #e5e5e5;
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
button.accept-policy {
font-size: 1rem;
line-height: inherit;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
white-space: nowrap;
line-height: 60px;
}
.container-custom {
margin-left: 1.5%;
margin-right: 1.5%;
}
.fake-card-wrapper {
position: relative;
width: 22em;
height: 13em;
z-index: 1;
-webkit-transition: all 0.4s linear;
/* Saf3.2+, Chrome */
-moz-transition: all 0.4s linear;
/* FF4+ */
-o-transition: all 0.4s linear;
/* Opera 10.5+ */
transition: all 0.4s linear;
-webkit-perspective: 1000;
/* Saf4+, Chrome 12+ */
-moz-perspective: 1000;
/* FF10+ */
-ms-perspective: 1000;
/* IE10+ */
perspective: 1000;
}
.fake-card-wrapper .fake-card {
animation: animatecard 5s ease-in-out 0s infinite normal forwards;
width: 100%;
height: 100%;
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
-ms-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-transition: all 0.4s linear;
/* Saf3.2+, Chrome */
-moz-transition: all 0.4s linear;
/* FF4+ */
-o-transition: all 0.4s linear;
/* Opera 10.5+ */
transition: all 0.4s linear;
}
.fake-card-wrapper .fake-card .face-card {
position: absolute;
display: block;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
backface-visibility: hidden;
}
.fake-card-wrapper .fake-card .face-card.traseira {
display: block;
-webkit-transform: rotateY(180deg);
-moz-transform: rotateY(180deg);
-ms-transform: rotateY(180deg);
transform: rotateY(180deg);
-webkit-box-sizing: border-box;
/* prev iOS4, prev Android 2.3 */
-moz-box-sizing: border-box;
/* FF1+ */
box-sizing: border-box;
/* Chrome, IE8, Opera, Safari 5.1*/
}
.validity {
opacity: 0.85;
display: inline-block;
margin-top: 10px;
font-weight: bold;
}
.fake-card-wrapper .fake-card {
margin: 0;
padding: 0;
font-family: "Trebuchet MS", "Ubuntu", sans-serif;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 1px 1px 20px rgba(0, 0, 0, 0.5);
border-radius: 15px;
background: #5D54CC;
color: white;
}
.fake-card-wrapper .fake-card .face-card {
border-radius: 15px;
}
.fake-card-wrapper .fake-card .face-card.front {
background: -webkit-linear-gradient(to bottom right, #083465, #554994);
background: -moz-linear-gradient(to bottom right, #0D4C92, #554994);
background: linear-gradient(to bottom right, #18375b, #554994);
}
.fake-card-wrapper .fake-card .face-card.front .chip {
width: 55px;
height: 40px;
position: absolute;
left: 20px;
top: 30px;
border-radius: 3px;
background: -webkit-linear-gradient(to top left, #AACAEF, #FDE7F9);
background: -moz-linear-gradient(to top left, #AACAEF, #FDE7F9);
background: linear-gradient(to top left, #AACAEF, #FDE7F9);
display: flex;
justify-content: center;
align-items: center;
}
.fake-card-wrapper .fake-card .face-card.front .chip div {
padding: 4px 6px;
background: -webkit-linear-gradient(to top left, #AACAEF, #FDE7F9);
background: -moz-linear-gradient(to top left, #AACAEF, #FDE7F9);
background: linear-gradient(to top left, #AACAEF, #FDE7F9);
box-shadow: 1px 1px 7px rgba(0, 0, 0, 0.1);
}
.fake-card-wrapper .fake-card .face-card.front .card-number {
font-family: "JetBrains Mono", "Courier New", Courier, monospace;
font-weight: 350;
position: absolute;
top: 85px;
left: 20px;
}
.fake-card-wrapper .fake-card .face-card.front .card-holder {
font-family: "Courier New", Courier, monospace;
font-weight: 600;
position: absolute;
left: 20px;
bottom: 41px;
}
.fake-card-wrapper .fake-card .face-card.front .validity {
position: absolute;
bottom: 20px;
left: 20px;
display: flex;
align-items: center;
}
.fake-card-wrapper .fake-card .face-card.front .validity .small {
font-size: 0.6em;
font-weight: bold;
line-height: 9px;
margin-right: 5px;
}
.fake-card-wrapper .fake-card .face-card.front .fake-card-logo-bottom {
position: absolute;
bottom: 33px;
right: 20px;
width: 70px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.fake-card-wrapper .fake-card .face-card.front .fake-card-logo-top {
position: absolute;
top: 25px;
right: 20px;
width: 70px;
height: 65px;
border-radius: 10px;
background-color: white;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.fake-card-wrapper .fake-card .face-card.front .fake-card-logo-top img {
width: 55px;
}
.fake-card-wrapper .fake-card .face-card.front .fake-card-logo-top img.fake-card-logo-inner {
margin-top: 1px;
margin-bottom: 2px;
}
.fake-card-wrapper .fake-card .face-card.front .fake-card-logo-bottom img {
width: 55px;
}
.fake-card-wrapper .fake-card .face-card.front .fake-card-logo-bottom img.fake-card-logo-inner {
margin-top: 18px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="svg3409"
inkscape:version="1.2.2 (b0a84865, 2022-12-01)"
sodipodi:docname="matecard-color-long copy.svg"
x="0px"
y="0px"
width="999.20001"
height="930.62659"
viewBox="0 0 999.2 930.62659"
enable-background="new 0 0 999.2 776"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs18" /><sodipodi:namedview
id="namedview16"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="0.63969603"
inkscape:cx="400.19007"
inkscape:cy="325.93605"
inkscape:window-width="2560"
inkscape:window-height="1359"
inkscape:window-x="0"
inkscape:window-y="309"
inkscape:window-maximized="1"
inkscape:current-layer="svg3409" />
<g
id="g13">
<rect
id="rect19"
x="364"
y="66.099998"
fill="#ff5a00"
width="270.39999"
height="485.79999"
style="fill:#ff8a4c;fill-opacity:1" />
<path
id="XMLID_330_"
inkscape:connector-curvature="0"
fill="#eb001b"
d="M 382,309 C 382,210.3 428.4,122.7 499.6,66.1 447.2,24.9 381.1,0 309,0 138.2,0 0,138.2 0,309 0,479.8 138.2,618 309,618 381.1,618 447.2,593.1 499.6,551.9 428.3,496.1 382,407.7 382,309 Z"
style="fill:#ea4659;fill-opacity:1" />
<path
id="path22"
inkscape:connector-curvature="0"
fill="#f79e1b"
d="M 999.2,309 C 999.2,479.8 861,618 690.2,618 618.1,618 552,593.1 499.6,551.9 571.7,495.2 617.2,407.7 617.2,309 617.2,210.3 570.8,122.7 499.6,66.1 551.9,24.9 618,0 690.1,0 861,0 999.2,139.1 999.2,309 Z"
style="fill:#f7bc65;fill-opacity:1" />
</g>
<g
id="g558"
transform="matrix(1.2163945,0,0,1.2163945,-122.52496,-13.295545)"
style="fill:#000000"><path
id="path502"
d="m 368.4,733.1 v -41.2 h -18 v 10.3 c -6,-7.7 -14.6,-12 -25.8,-12 -23.2,0 -41.2,18 -41.2,42.9 0,24.9 18,42.9 41.2,42.9 12,0 20.6,-4.3 25.8,-12 v 10.3 h 18 z m -66.9,0 c 0,-14.6 9.4,-26.6 24.9,-26.6 14.6,0 24.9,11.2 24.9,26.6 0,14.6 -10.3,26.6 -24.9,26.6 -15.5,-0.9 -24.9,-12.1 -24.9,-26.6 z"
style="fill:#000000" /><path
id="path498"
d="m 887.1,733.1 v -73.8 h -18 v 42.9 c -6,-7.7 -14.6,-12 -25.8,-12 -23.2,0 -41.2,18 -41.2,42.9 0,24.9 18,42.9 41.2,42.9 12,0 20.6,-4.3 25.8,-12 v 10.3 h 18 z m -66.1,0 c 0,-14.6 9.4,-26.6 24.9,-26.6 14.6,0 24.9,11.2 24.9,26.6 0,14.6 -10.3,26.6 -24.9,26.6 C 830.4,758.8 821,747.6 821,733.1 Z"
style="fill:#000000" /><path
id="path494"
d="m 503.9,690.1 c -24,0 -41.2,17.2 -41.2,42.9 0,25.8 17.2,42.9 42.1,42.9 12,0 24,-3.4 33.5,-11.2 l -8.6,-12.9 c -6.9,5.2 -15.5,8.6 -24,8.6 -11.2,0 -22.3,-5.2 -24.9,-19.7 h 60.9 c 0,-2.6 0,-4.3 0,-6.9 0.8,-26.5 -14.7,-43.7 -37.8,-43.7 z m 0,15.5 c 11.2,0 18.9,6.9 20.6,19.7 h -42.9 c 1.7,-11.1 9.4,-19.7 22.3,-19.7 z"
style="fill:#000000" /><path
id="path490"
d="m 721.4,733.1 v -41.2 h -18 v 10.3 c -6,-7.7 -14.6,-12 -25.8,-12 -23.2,0 -41.2,18 -41.2,42.9 0,24.9 18,42.9 41.2,42.9 12,0 20.6,-4.3 25.8,-12 v 10.3 h 18 z m -66.1,0 c 0,-14.6 9.4,-26.6 24.9,-26.6 14.6,0 24.9,11.2 24.9,26.6 0,14.6 -10.3,26.6 -24.9,26.6 -15.4,-0.9 -24.9,-12.1 -24.9,-26.6 z"
style="fill:#000000" /><path
d="m 554.9,733.1 c 0,24.9 17.2,42.9 43.8,42.9 12,0 20.6,-2.6 29.2,-9.4 L 619.3,752 c -6.9,5.2 -13.7,7.7 -21.5,7.7 -14.6,0 -24.9,-10.3 -24.9,-26.6 0,-15.5 10.3,-25.8 24.9,-26.6 7.7,0 14.6,2.6 21.5,7.7 l 8.6,-14.6 c -8.6,-6.9 -17.2,-9.4 -29.2,-9.4 -26.6,-0.1 -43.8,18 -43.8,42.9 z"
id="path488"
style="fill:#000000" /><path
d="m 784.9,690.1 c -10.3,0 -17.2,5.2 -21.5,12 v -10.3 h -18 v 82.4 h 18 v -46.4 c 0,-13.7 6,-21.5 17.2,-21.5 3.4,0 7.7,0.9 11.2,1.7 l 5.2,-17 c -3.5,-0.9 -8.6,-0.9 -12.1,-0.9 z"
id="path486"
style="fill:#000000" /><path
d="M 448.1,691.9 H 418.9 V 667 h -18 v 24.9 h -16.3 v 16.3 h 16.3 V 746 c 0,18.9 7.7,30 28.3,30 7.7,0 16.3,-2.6 22.3,-6 l -5.2,-15.5 c -5.2,3.4 -11.2,4.3 -15.5,4.3 -8.6,0 -12,-5.2 -12,-13.7 V 708.2 H 448 v -16.3 z"
id="path480"
style="fill:#000000" /><path
d="m 267.1,774.3 v -51.5 c 0,-19.7 -12,-32.6 -32.6,-32.6 -10.3,0 -21.5,3.4 -29.2,14.6 -6,-9.4 -14.6,-14.6 -27.5,-14.6 -8.6,0 -17.2,2.6 -24,12 v -10.3 h -18 v 82.4 h 18 v -45.5 c 0,-14.6 7.7,-21.5 19.7,-21.5 12,0 18,7.7 18,21.5 v 45.5 h 18 v -45.5 c 0,-14.6 8.6,-21.5 19.7,-21.5 12,0 18,7.7 18,21.5 v 45.5 z"
id="path448"
style="fill:#000000" /></g></svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="svg3409"
inkscape:version="1.2.2 (b0a84865, 2022-12-01)"
sodipodi:docname="matecard-color-long-new.svg"
x="0px"
y="0px"
width="999.20001"
height="930.62659"
viewBox="0 0 999.2 930.62659"
enable-background="new 0 0 999.2 776"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs18" /><sodipodi:namedview
id="namedview16"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="0.63969603"
inkscape:cx="398.62683"
inkscape:cy="325.93605"
inkscape:window-width="2560"
inkscape:window-height="1359"
inkscape:window-x="0"
inkscape:window-y="309"
inkscape:window-maximized="1"
inkscape:current-layer="g13" />
<g
id="g13">
<rect
id="rect19"
x="364"
y="66.099998"
fill="#ff5a00"
width="270.39999"
height="485.79999"
style="fill:#ff8a4c;fill-opacity:1" />
<path
id="XMLID_330_"
inkscape:connector-curvature="0"
fill="#eb001b"
d="M 382,309 C 382,210.3 428.4,122.7 499.6,66.1 447.2,24.9 381.1,0 309,0 138.2,0 0,138.2 0,309 0,479.8 138.2,618 309,618 381.1,618 447.2,593.1 499.6,551.9 428.3,496.1 382,407.7 382,309 Z"
style="fill:#ea4659;fill-opacity:1" />
<path
id="path22"
inkscape:connector-curvature="0"
fill="#f79e1b"
d="M 999.2,309 C 999.2,479.8 861,618 690.2,618 618.1,618 552,593.1 499.6,551.9 571.7,495.2 617.2,407.7 617.2,309 617.2,210.3 570.8,122.7 499.6,66.1 551.9,24.9 618,0 690.1,0 861,0 999.2,139.1 999.2,309 Z"
style="fill:#f7bc65;fill-opacity:1" />
</g>
<g
id="g558"
transform="matrix(1.2163945,0,0,1.2163945,-122.52496,-13.295545)"
style="fill:#ffffff"><path
id="path502"
d="m 368.4,733.1 v -41.2 h -18 v 10.3 c -6,-7.7 -14.6,-12 -25.8,-12 -23.2,0 -41.2,18 -41.2,42.9 0,24.9 18,42.9 41.2,42.9 12,0 20.6,-4.3 25.8,-12 v 10.3 h 18 z m -66.9,0 c 0,-14.6 9.4,-26.6 24.9,-26.6 14.6,0 24.9,11.2 24.9,26.6 0,14.6 -10.3,26.6 -24.9,26.6 -15.5,-0.9 -24.9,-12.1 -24.9,-26.6 z"
style="fill:#ffffff" /><path
id="path498"
d="m 887.1,733.1 v -73.8 h -18 v 42.9 c -6,-7.7 -14.6,-12 -25.8,-12 -23.2,0 -41.2,18 -41.2,42.9 0,24.9 18,42.9 41.2,42.9 12,0 20.6,-4.3 25.8,-12 v 10.3 h 18 z m -66.1,0 c 0,-14.6 9.4,-26.6 24.9,-26.6 14.6,0 24.9,11.2 24.9,26.6 0,14.6 -10.3,26.6 -24.9,26.6 C 830.4,758.8 821,747.6 821,733.1 Z"
style="fill:#ffffff" /><path
id="path494"
d="m 503.9,690.1 c -24,0 -41.2,17.2 -41.2,42.9 0,25.8 17.2,42.9 42.1,42.9 12,0 24,-3.4 33.5,-11.2 l -8.6,-12.9 c -6.9,5.2 -15.5,8.6 -24,8.6 -11.2,0 -22.3,-5.2 -24.9,-19.7 h 60.9 c 0,-2.6 0,-4.3 0,-6.9 0.8,-26.5 -14.7,-43.7 -37.8,-43.7 z m 0,15.5 c 11.2,0 18.9,6.9 20.6,19.7 h -42.9 c 1.7,-11.1 9.4,-19.7 22.3,-19.7 z"
style="fill:#ffffff" /><path
id="path490"
d="m 721.4,733.1 v -41.2 h -18 v 10.3 c -6,-7.7 -14.6,-12 -25.8,-12 -23.2,0 -41.2,18 -41.2,42.9 0,24.9 18,42.9 41.2,42.9 12,0 20.6,-4.3 25.8,-12 v 10.3 h 18 z m -66.1,0 c 0,-14.6 9.4,-26.6 24.9,-26.6 14.6,0 24.9,11.2 24.9,26.6 0,14.6 -10.3,26.6 -24.9,26.6 -15.4,-0.9 -24.9,-12.1 -24.9,-26.6 z"
style="fill:#ffffff" /><path
d="m 554.9,733.1 c 0,24.9 17.2,42.9 43.8,42.9 12,0 20.6,-2.6 29.2,-9.4 L 619.3,752 c -6.9,5.2 -13.7,7.7 -21.5,7.7 -14.6,0 -24.9,-10.3 -24.9,-26.6 0,-15.5 10.3,-25.8 24.9,-26.6 7.7,0 14.6,2.6 21.5,7.7 l 8.6,-14.6 c -8.6,-6.9 -17.2,-9.4 -29.2,-9.4 -26.6,-0.1 -43.8,18 -43.8,42.9 z"
id="path488"
style="fill:#ffffff" /><path
d="m 784.9,690.1 c -10.3,0 -17.2,5.2 -21.5,12 v -10.3 h -18 v 82.4 h 18 v -46.4 c 0,-13.7 6,-21.5 17.2,-21.5 3.4,0 7.7,0.9 11.2,1.7 l 5.2,-17 c -3.5,-0.9 -8.6,-0.9 -12.1,-0.9 z"
id="path486"
style="fill:#ffffff" /><path
d="M 448.1,691.9 H 418.9 V 667 h -18 v 24.9 h -16.3 v 16.3 h 16.3 V 746 c 0,18.9 7.7,30 28.3,30 7.7,0 16.3,-2.6 22.3,-6 l -5.2,-15.5 c -5.2,3.4 -11.2,4.3 -15.5,4.3 -8.6,0 -12,-5.2 -12,-13.7 V 708.2 H 448 v -16.3 z"
id="path480"
style="fill:#ffffff" /><path
d="m 267.1,774.3 v -51.5 c 0,-19.7 -12,-32.6 -32.6,-32.6 -10.3,0 -21.5,3.4 -29.2,14.6 -6,-9.4 -14.6,-14.6 -27.5,-14.6 -8.6,0 -17.2,2.6 -24,12 v -10.3 h -18 v 82.4 h 18 v -45.5 c 0,-14.6 7.7,-21.5 19.7,-21.5 12,0 18,7.7 18,21.5 v 45.5 h 18 v -45.5 c 0,-14.6 8.6,-21.5 19.7,-21.5 12,0 18,7.7 18,21.5 v 45.5 z"
id="path448"
style="fill:#ffffff" /></g></svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="svg3409"
inkscape:version="1.2.2 (b0a84865, 2022-12-01)"
sodipodi:docname="matecard-color.svg"
x="0px"
y="0px"
width="999.2px"
height="776px"
viewBox="0 0 999.2 776"
enable-background="new 0 0 999.2 776"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs18" /><sodipodi:namedview
id="namedview16"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="0.30412371"
inkscape:cx="376.49153"
inkscape:cy="391.28814"
inkscape:window-width="1309"
inkscape:window-height="456"
inkscape:window-x="0"
inkscape:window-y="804"
inkscape:window-maximized="0"
inkscape:current-layer="g13" />
<g
id="g13">
<rect
id="rect19"
x="364"
y="66.1"
fill="#FF5A00"
width="270.4"
height="485.8"
style="fill:#ff8a4c;fill-opacity:1" />
<path
id="XMLID_330_"
inkscape:connector-curvature="0"
fill="#EB001B"
d="M382,309c0-98.7,46.4-186.3,117.6-242.9 C447.2,24.9,381.1,0,309,0C138.2,0,0,138.2,0,309s138.2,309,309,309c72.1,0,138.2-24.9,190.6-66.1C428.3,496.1,382,407.7,382,309z"
style="fill:#ea4659;fill-opacity:1" />
<path
id="path22"
inkscape:connector-curvature="0"
fill="#F79E1B"
d="M999.2,309c0,170.8-138.2,309-309,309 c-72.1,0-138.2-24.9-190.6-66.1c72.1-56.7,117.6-144.2,117.6-242.9S570.8,122.7,499.6,66.1C551.9,24.9,618,0,690.1,0 C861,0,999.2,139.1,999.2,309z"
style="fill:#f7bc65;fill-opacity:1" />
</g>
<g
id="g558"
transform="translate(-11.849994)"><path
id="path502"
d="m 368.4,733.1 v -41.2 h -18 v 10.3 c -6,-7.7 -14.6,-12 -25.8,-12 -23.2,0 -41.2,18 -41.2,42.9 0,24.9 18,42.9 41.2,42.9 12,0 20.6,-4.3 25.8,-12 v 10.3 h 18 z m -66.9,0 c 0,-14.6 9.4,-26.6 24.9,-26.6 14.6,0 24.9,11.2 24.9,26.6 0,14.6 -10.3,26.6 -24.9,26.6 -15.5,-0.9 -24.9,-12.1 -24.9,-26.6 z" /><path
id="path498"
d="m 887.1,733.1 v -73.8 h -18 v 42.9 c -6,-7.7 -14.6,-12 -25.8,-12 -23.2,0 -41.2,18 -41.2,42.9 0,24.9 18,42.9 41.2,42.9 12,0 20.6,-4.3 25.8,-12 v 10.3 h 18 z m -66.1,0 c 0,-14.6 9.4,-26.6 24.9,-26.6 14.6,0 24.9,11.2 24.9,26.6 0,14.6 -10.3,26.6 -24.9,26.6 C 830.4,758.8 821,747.6 821,733.1 Z" /><path
id="path494"
d="m 503.9,690.1 c -24,0 -41.2,17.2 -41.2,42.9 0,25.8 17.2,42.9 42.1,42.9 12,0 24,-3.4 33.5,-11.2 l -8.6,-12.9 c -6.9,5.2 -15.5,8.6 -24,8.6 -11.2,0 -22.3,-5.2 -24.9,-19.7 h 60.9 c 0,-2.6 0,-4.3 0,-6.9 0.8,-26.5 -14.7,-43.7 -37.8,-43.7 z m 0,15.5 c 11.2,0 18.9,6.9 20.6,19.7 h -42.9 c 1.7,-11.1 9.4,-19.7 22.3,-19.7 z" /><path
id="path490"
d="m 721.4,733.1 v -41.2 h -18 v 10.3 c -6,-7.7 -14.6,-12 -25.8,-12 -23.2,0 -41.2,18 -41.2,42.9 0,24.9 18,42.9 41.2,42.9 12,0 20.6,-4.3 25.8,-12 v 10.3 h 18 z m -66.1,0 c 0,-14.6 9.4,-26.6 24.9,-26.6 14.6,0 24.9,11.2 24.9,26.6 0,14.6 -10.3,26.6 -24.9,26.6 -15.4,-0.9 -24.9,-12.1 -24.9,-26.6 z" /><path
d="m 554.9,733.1 c 0,24.9 17.2,42.9 43.8,42.9 12,0 20.6,-2.6 29.2,-9.4 L 619.3,752 c -6.9,5.2 -13.7,7.7 -21.5,7.7 -14.6,0 -24.9,-10.3 -24.9,-26.6 0,-15.5 10.3,-25.8 24.9,-26.6 7.7,0 14.6,2.6 21.5,7.7 l 8.6,-14.6 c -8.6,-6.9 -17.2,-9.4 -29.2,-9.4 -26.6,-0.1 -43.8,18 -43.8,42.9 z"
id="path488" /><path
d="m 784.9,690.1 c -10.3,0 -17.2,5.2 -21.5,12 v -10.3 h -18 v 82.4 h 18 v -46.4 c 0,-13.7 6,-21.5 17.2,-21.5 3.4,0 7.7,0.9 11.2,1.7 l 5.2,-17 c -3.5,-0.9 -8.6,-0.9 -12.1,-0.9 z"
id="path486" /><path
d="M 448.1,691.9 H 418.9 V 667 h -18 v 24.9 h -16.3 v 16.3 h 16.3 V 746 c 0,18.9 7.7,30 28.3,30 7.7,0 16.3,-2.6 22.3,-6 l -5.2,-15.5 c -5.2,3.4 -11.2,4.3 -15.5,4.3 -8.6,0 -12,-5.2 -12,-13.7 V 708.2 H 448 v -16.3 z"
id="path480" /><path
d="m 267.1,774.3 v -51.5 c 0,-19.7 -12,-32.6 -32.6,-32.6 -10.3,0 -21.5,3.4 -29.2,14.6 -6,-9.4 -14.6,-14.6 -27.5,-14.6 -8.6,0 -17.2,2.6 -24,12 v -10.3 h -18 v 82.4 h 18 v -45.5 c 0,-14.6 7.7,-21.5 19.7,-21.5 12,0 18,7.7 18,21.5 v 45.5 h 18 v -45.5 c 0,-14.6 8.6,-21.5 19.7,-21.5 12,0 18,7.7 18,21.5 v 45.5 z"
id="path448" /></g></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -0,0 +1,4 @@
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
// for details on configuring this project to bundle and minify static web assets.
// Write your JavaScript code.

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,427 @@
/*!
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
background-color: currentColor;
border: 0;
opacity: 0.25;
}
hr:not([size]) {
height: 1px;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-bs-original-title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-left: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.2em;
background-color: #fcf8e3;
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: #0d6efd;
text-decoration: underline;
}
a:hover {
color: #0a58ca;
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
direction: ltr /* rtl:ignore */;
unicode-bidi: bidi-override;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: #d63384;
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.2rem 0.4rem;
font-size: 0.875em;
color: #fff;
background-color: #212529;
border-radius: 0.2rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
font-weight: 700;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: #6c757d;
text-align: left;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]::-webkit-calendar-picker-indicator {
display: none;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: left;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: left;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
outline-offset: -2px;
-webkit-appearance: textfield;
}
/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::file-selector-button {
font: inherit;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,424 @@
/*!
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
background-color: currentColor;
border: 0;
opacity: 0.25;
}
hr:not([size]) {
height: 1px;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-bs-original-title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-right: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-right: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.2em;
background-color: #fcf8e3;
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: #0d6efd;
text-decoration: underline;
}
a:hover {
color: #0a58ca;
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
direction: ltr ;
unicode-bidi: bidi-override;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: #d63384;
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.2rem 0.4rem;
font-size: 0.875em;
color: #fff;
background-color: #212529;
border-radius: 0.2rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
font-weight: 700;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: #6c757d;
text-align: right;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]::-webkit-calendar-picker-indicator {
display: none;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: right;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: right;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
outline-offset: -2px;
-webkit-appearance: textfield;
}
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::file-selector-button {
font: inherit;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-right:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-right:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:right}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:right;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:right}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}[type=email],[type=number],[type=tel],[type=url]{direction:ltr}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.rtl.min.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,4 @@
/*! DataTables Bootstrap 5 integration
* 2020 SpryMedia Ltd - datatables.net/license
*/
!function(t){"function"==typeof define&&define.amd?define(["jquery","datatables.net"],function(e){return t(e,window,document)}):"object"==typeof exports?module.exports=function(e,a){return e=e||window,(a=a||("undefined"!=typeof window?require("jquery"):require("jquery")(e))).fn.dataTable||require("datatables.net")(e,a),t(a,0,e.document)}:t(jQuery,window,document)}(function(x,e,r,s){"use strict";var o=x.fn.dataTable;return x.extend(!0,o.defaults,{dom:"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>><'row dt-row'<'col-sm-12'tr>><'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",renderer:"bootstrap"}),x.extend(o.ext.classes,{sWrapper:"dataTables_wrapper dt-bootstrap5",sFilterInput:"form-control form-control-sm",sLengthSelect:"form-select form-select-sm",sProcessing:"dataTables_processing card",sPageButton:"paginate_button page-item"}),o.ext.renderer.pageButton.bootstrap=function(i,e,d,a,l,c){function u(e,a){for(var t,n,r=function(e){e.preventDefault(),x(e.currentTarget).hasClass("disabled")||b.page()==e.data.action||b.page(e.data.action).draw("page")},s=0,o=a.length;s<o;s++)if(n=a[s],Array.isArray(n))u(e,n);else{switch(f=p="",n){case"ellipsis":p="&#x2026;",f="disabled";break;case"first":p=m.sFirst,f=n+(0<l?"":" disabled");break;case"previous":p=m.sPrevious,f=n+(0<l?"":" disabled");break;case"next":p=m.sNext,f=n+(l<c-1?"":" disabled");break;case"last":p=m.sLast,f=n+(l<c-1?"":" disabled");break;default:p=n+1,f=l===n?"active":""}p&&(t=x("<li>",{class:g.sPageButton+" "+f,id:0===d&&"string"==typeof n?i.sTableId+"_"+n:null}).append(x("<a>",{href:"#","aria-controls":i.sTableId,"aria-label":w[n],"data-dt-idx":n,tabindex:i.iTabIndex,class:"page-link"}).html(p)).appendTo(e),i.oApi._fnBindAction(t,{action:n},r))}}var p,f,t,b=new o.Api(i),g=i.oClasses,m=i.oLanguage.oPaginate,w=i.oLanguage.oAria.paginate||{},e=x(e);try{t=e.find(r.activeElement).data("dt-idx")}catch(e){}var n=e.children("ul.pagination");n.length?n.empty():n=e.html("<ul/>").children("ul").addClass("pagination"),u(n,a),t!==s&&e.find("[data-dt-idx="+t+"]").trigger("focus")},o});

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show more