Compare commits
5 commits
Author | SHA1 | Date | |
---|---|---|---|
6333561801 | |||
41c182b58f | |||
5a7e9c0195 | |||
85aa8ad33d | |||
9adf6900d6 |
|
@ -1,3 +1,4 @@
|
|||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
not ie 11
|
||||
|
|
5
.editorconfig
Normal file
|
@ -0,0 +1,5 @@
|
|||
[*.{js,jsx,ts,tsx,vue}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
22
.eslintrc.js
|
@ -1,14 +1,20 @@
|
|||
/**
|
||||
* .eslint.js
|
||||
*
|
||||
* ESLint configuration file.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
extends: ["plugin:vue/essential", "eslint:recommended"],
|
||||
parserOptions: {
|
||||
parser: "babel-eslint"
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
],
|
||||
rules: {
|
||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
|
||||
}
|
||||
'vue/multi-word-component-names': 'off',
|
||||
},
|
||||
}
|
||||
|
|
1
.gitignore
vendored
|
@ -2,7 +2,6 @@
|
|||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
|
84
README.md
|
@ -1,29 +1,81 @@
|
|||
# ui
|
||||
# Vuetify (Default)
|
||||
|
||||
## Project setup
|
||||
This is the official scaffolding tool for Vuetify, designed to give you a head start in building your new Vuetify application. It sets up a base template with all the necessary configurations and standard directory structure, enabling you to begin development without the hassle of setting up the project from scratch.
|
||||
|
||||
```
|
||||
yarn install
|
||||
## ❗️ Important Links
|
||||
|
||||
- 📄 [Docs](https://vuetifyjs.com/)
|
||||
- 🚨 [Issues](https://issues.vuetifyjs.com/)
|
||||
- 🏬 [Store](https://store.vuetifyjs.com/)
|
||||
- 🎮 [Playground](https://play.vuetifyjs.com/)
|
||||
- 💬 [Discord](https://community.vuetifyjs.com)
|
||||
|
||||
## 💿 Install
|
||||
|
||||
Set up your project using your preferred package manager. Use the corresponding command to install the dependencies:
|
||||
|
||||
| Package Manager | Command |
|
||||
|---------------------------------------------------------------|----------------|
|
||||
| [yarn](https://yarnpkg.com/getting-started) | `yarn install` |
|
||||
| [npm](https://docs.npmjs.com/cli/v7/commands/npm-install) | `npm install` |
|
||||
| [pnpm](https://pnpm.io/installation) | `pnpm install` |
|
||||
| [bun](https://bun.sh/#getting-started) | `bun install` |
|
||||
|
||||
After completing the installation, your environment is ready for Vuetify development.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- 🖼️ **Optimized Front-End Stack**: Leverage the latest Vue 3 and Vuetify 3 for a modern, reactive UI development experience. [Vue 3](https://v3.vuejs.org/) | [Vuetify 3](https://vuetifyjs.com/en/)
|
||||
- 🗃️ **State Management**: Integrated with [Pinia](https://pinia.vuejs.org/), the intuitive, modular state management solution for Vue.
|
||||
- 🚦 **Routing and Layouts**: Utilizes Vue Router for SPA navigation and vite-plugin-vue-layouts for organizing Vue file layouts. [Vue Router](https://router.vuejs.org/) | [vite-plugin-vue-layouts](https://github.com/JohnCampionJr/vite-plugin-vue-layouts)
|
||||
- 💻 **Enhanced Development Experience**: Benefit from TypeScript's static type checking and the ESLint plugin suite for Vue, ensuring code quality and consistency. [TypeScript](https://www.typescriptlang.org/) | [ESLint Plugin Vue](https://eslint.vuejs.org/)
|
||||
- ⚡ **Next-Gen Tooling**: Powered by Vite, experience fast cold starts and instant HMR (Hot Module Replacement). [Vite](https://vitejs.dev/)
|
||||
- 🧩 **Automated Component Importing**: Streamline your workflow with unplugin-vue-components, automatically importing components as you use them. [unplugin-vue-components](https://github.com/antfu/unplugin-vue-components)
|
||||
- 🛠️ **Strongly-Typed Vue**: Use vue-tsc for type-checking your Vue components, and enjoy a robust development experience. [vue-tsc](https://github.com/johnsoncodehk/volar/tree/master/packages/vue-tsc)
|
||||
|
||||
These features are curated to provide a seamless development experience from setup to deployment, ensuring that your Vuetify application is both powerful and maintainable.
|
||||
|
||||
## 💡 Usage
|
||||
|
||||
This section covers how to start the development server and build your project for production.
|
||||
|
||||
### Starting the Development Server
|
||||
|
||||
To start the development server with hot-reload, run the following command. The server will be accessible at [http://localhost:3000](http://localhost:3000):
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
(Repeat for npm, pnpm, and bun with respective commands.)
|
||||
|
||||
```
|
||||
yarn serve
|
||||
```
|
||||
> Add NODE_OPTIONS='--no-warnings' to suppress the JSON import warnings that happen as part of the Vuetify import mapping. If you are on Node [v21.3.0](https://nodejs.org/en/blog/release/v21.3.0) or higher, you can change this to NODE_OPTIONS='--disable-warning=5401'. If you don't mind the warning, you can remove this from your package.json dev script.
|
||||
|
||||
### Compiles and minifies for production
|
||||
### Building for Production
|
||||
|
||||
```
|
||||
To build your project for production, use:
|
||||
|
||||
```bash
|
||||
yarn build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
(Repeat for npm, pnpm, and bun with respective commands.)
|
||||
|
||||
```
|
||||
yarn lint
|
||||
```
|
||||
Once the build process is completed, your application will be ready for deployment in a production environment.
|
||||
|
||||
### Customize configuration
|
||||
## 💪 Support Vuetify Development
|
||||
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
This project is built with [Vuetify](https://vuetifyjs.com/en/), a UI Library with a comprehensive collection of Vue components. Vuetify is an MIT licensed Open Source project that has been made possible due to the generous contributions by our [sponsors and backers](https://vuetifyjs.com/introduction/sponsors-and-backers/). If you are interested in supporting this project, please consider:
|
||||
|
||||
- [Requesting Enterprise Support](https://support.vuetifyjs.com/)
|
||||
- [Sponsoring John on Github](https://github.com/users/johnleider/sponsorship)
|
||||
- [Sponsoring Kael on Github](https://github.com/users/kaelwd/sponsorship)
|
||||
- [Supporting the team on Open Collective](https://opencollective.com/vuetify)
|
||||
- [Becoming a sponsor on Patreon](https://www.patreon.com/vuetify)
|
||||
- [Becoming a subscriber on Tidelift](https://tidelift.com/subscription/npm/vuetify)
|
||||
- [Making a one-time donation with Paypal](https://paypal.me/vuetify)
|
||||
|
||||
## 📑 License
|
||||
[MIT](http://opensource.org/licenses/MIT)
|
||||
|
||||
Copyright (c) 2016-present Vuetify, LLC
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
presets: ["@vue/cli-plugin-babel/preset"]
|
||||
}
|
30
codegen.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2024. Matthew Nichols. SAT Project - FlowForms. This contains the code for the FlowForms project.
|
||||
*/
|
||||
|
||||
import type { CodegenConfig } from "@graphql-codegen/cli"
|
||||
|
||||
const config: CodegenConfig = {
|
||||
overwrite: true,
|
||||
schema: "http://localhost:24007/graphql",
|
||||
documents: [
|
||||
"src/**/*.graphql.ts",
|
||||
"src/**/*.mutation.ts",
|
||||
"src/**/*.query.ts",
|
||||
"src/**/*.fragment.ts",
|
||||
"src/**/*.subscription.ts"
|
||||
],
|
||||
generates: {
|
||||
"./graphql.schema.json": {
|
||||
plugins: ["introspection"]
|
||||
},
|
||||
"./src/gql/": {
|
||||
preset: "client",
|
||||
config: {
|
||||
useTypeImports: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default config
|
18
components.d.ts
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ChipTag: typeof import('./src/components/ChipTag.vue')['default']
|
||||
DiscordLogo: typeof import('./src/components/DiscordLogo.vue')['default']
|
||||
FlowinityLogo: typeof import('./src/components/FlowinityLogo.vue')['default']
|
||||
FlowinityWordmark: typeof import('./src/components/FlowinityWordmark.vue')['default']
|
||||
Header: typeof import('./src/components/Header.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
}
|
1630
graphql.schema.json
Normal file
16
index.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Troplo's Website</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
79
package.json
|
@ -1,43 +1,52 @@
|
|||
{
|
||||
"name": "troplo-site",
|
||||
"version": "1.0.13",
|
||||
"private": true,
|
||||
"name": "troplo-website-next",
|
||||
"version": "2.0.1",
|
||||
"scripts": {
|
||||
"serve": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve",
|
||||
"build": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"postinstall": "patch-package"
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --fix --ignore-path .gitignore",
|
||||
"codegen": "graphql-codegen --config ./codegen.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.6.5",
|
||||
"node-ipc": "git+https://git.troplo.com/Troplo/node-ipc",
|
||||
"peacenotwar": "git+https://git.troplo.com/Troplo/peacenotwar",
|
||||
"vue": "^2.6.11",
|
||||
"vue-matomo": "^4.1.0",
|
||||
"vue-router": "^3.2.0",
|
||||
"vuetify": "2.5.10",
|
||||
"vuex": "^3.4.0"
|
||||
"@apollo/client": "^3.11.8",
|
||||
"@graphql-codegen/cli": "^5.0.2",
|
||||
"@graphql-codegen/introspection": "^4.0.3",
|
||||
"@mdi/font": "^7.4.47",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@vue/apollo-composable": "^4.2.1",
|
||||
"core-js": "^3.37.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"graphql": "^16.9.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"markdown-it": "^14.1.0",
|
||||
"pinia": "^2.2.2",
|
||||
"prettier": "^3.3.3",
|
||||
"roboto-fontface": "*",
|
||||
"vue": "^3.4.31",
|
||||
"vuetify": "^3.6.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "~4.5.0",
|
||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"patch-package": "^6.4.7",
|
||||
"prettier": "^2.5.1",
|
||||
"sass": "~1.32.0",
|
||||
"sass-loader": "^10.0.0",
|
||||
"vue-cli-plugin-vuetify": "~2.4.2",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuetify-loader": "^1.7.0",
|
||||
"webpack-auto-inject-version": "^1.2.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"peacenotwar": "git+https://git.troplo.com/Troplo/peacenotwar",
|
||||
"node-ipc": "git+https://git.troplo.com/Troplo/node-ipc"
|
||||
"@babel/types": "^7.24.7",
|
||||
"@types/node": "^20.14.10",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vue/eslint-config-typescript": "^13.0.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-n": "^16.6.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^6.4.0",
|
||||
"eslint-plugin-vue": "^9.27.0",
|
||||
"react": "^18.3.1",
|
||||
"sass": "1.77.6",
|
||||
"typescript": "^5.4.2",
|
||||
"unplugin-fonts": "^1.1.1",
|
||||
"unplugin-vue-components": "^0.27.2",
|
||||
"unplugin-vue-router": "^0.10.0",
|
||||
"vite": "^5.3.3",
|
||||
"vite-plugin-vuetify": "^2.0.3",
|
||||
"vue-router": "^4.4.0",
|
||||
"vue-tsc": "^2.0.26"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,438 +0,0 @@
|
|||
diff --git a/node_modules/vuetify/src/components/VNavigationDrawer/VNavigationDrawer.sass b/node_modules/vuetify/src/components/VNavigationDrawer/VNavigationDrawer.sass
|
||||
index ee4419a..6d1ba3d 100644
|
||||
--- a/node_modules/vuetify/src/components/VNavigationDrawer/VNavigationDrawer.sass
|
||||
+++ b/node_modules/vuetify/src/components/VNavigationDrawer/VNavigationDrawer.sass
|
||||
@@ -9,7 +9,7 @@
|
||||
background-color: map-get($material, 'dividers')
|
||||
|
||||
.v-divider
|
||||
- border-color: map-get($material, 'dividers')
|
||||
+ width: 0
|
||||
|
||||
// Block
|
||||
.v-navigation-drawer
|
||||
diff --git a/node_modules/vuetify/src/styles/settings/_dark.scss b/node_modules/vuetify/src/styles/settings/_dark.scss
|
||||
index 753d70d..12980cc 100644
|
||||
--- a/node_modules/vuetify/src/styles/settings/_dark.scss
|
||||
+++ b/node_modules/vuetify/src/styles/settings/_dark.scss
|
||||
@@ -6,7 +6,7 @@ $material-dark-elevation-colors: () !default;
|
||||
$material-dark-elevation-colors: map-deep-merge(
|
||||
(
|
||||
'0': #000000,
|
||||
- '1': #1E1E1E,
|
||||
+ '1': #151515,
|
||||
'2': #222222,
|
||||
'3': #252525,
|
||||
'4': #272727,
|
||||
diff --git a/node_modules/vuetify/src/styles/settings/_variables.scss b/node_modules/vuetify/src/styles/settings/_variables.scss
|
||||
index f5cc26b..6b998f1 100644
|
||||
--- a/node_modules/vuetify/src/styles/settings/_variables.scss
|
||||
+++ b/node_modules/vuetify/src/styles/settings/_variables.scss
|
||||
@@ -2,23 +2,24 @@
|
||||
|
||||
$color-pack: true !default;
|
||||
|
||||
-$body-font-family: 'Roboto', sans-serif !default;
|
||||
+$body-font-family: 'Montserrat', sans-serif !default;
|
||||
$font-size-root: 16px !default;
|
||||
$line-height-root: 1.5 !default;
|
||||
$border-radius-root: 4px !default;
|
||||
|
||||
$rounded: () !default;
|
||||
$rounded: map-deep-merge(
|
||||
- (
|
||||
- 0: 0,
|
||||
- 'sm': $border-radius-root / 2,
|
||||
- null: $border-radius-root,
|
||||
- 'lg': $border-radius-root * 2,
|
||||
- 'xl': $border-radius-root * 6,
|
||||
- 'pill': 9999px,
|
||||
- 'circle': 50%
|
||||
- ),
|
||||
- $rounded
|
||||
+ (
|
||||
+ 0: 0,
|
||||
+ 'sm': $border-radius-root / 2,
|
||||
+ null: $border-radius-root,
|
||||
+ 'lg': $border-radius-root * 2,
|
||||
+ 'xl': $border-radius-root * 3,
|
||||
+ 'xxl': $border-radius-root * 6,
|
||||
+ 'pill': 9999px,
|
||||
+ 'circle': 50%
|
||||
+ ),
|
||||
+ $rounded
|
||||
);
|
||||
|
||||
$spacer: 4px !default;
|
||||
@@ -39,14 +40,14 @@ $negative-spacers: () !default;
|
||||
|
||||
$grid-breakpoints: () !default;
|
||||
$grid-breakpoints: map-deep-merge(
|
||||
- (
|
||||
- 'xs': 0,
|
||||
- 'sm': 600px,
|
||||
- 'md': 960px,
|
||||
- 'lg': 1280px - 16px,
|
||||
- 'xl': 1920px - 16px
|
||||
- ),
|
||||
- $grid-breakpoints
|
||||
+ (
|
||||
+ 'xs': 0,
|
||||
+ 'sm': 600px,
|
||||
+ 'md': 960px,
|
||||
+ 'lg': 1280px - 16px,
|
||||
+ 'xl': 1920px - 16px
|
||||
+ ),
|
||||
+ $grid-breakpoints
|
||||
);
|
||||
|
||||
$grid-gutter: $spacer * 6 !default;
|
||||
@@ -57,191 +58,191 @@ $container-padding-x: $grid-gutter / 2 !default;
|
||||
|
||||
$grid-gutters: () !default;
|
||||
$grid-gutters: map-deep-merge(
|
||||
- (
|
||||
- 'xs': $grid-gutter / 12,
|
||||
- 'sm': $grid-gutter / 6,
|
||||
- 'md': $grid-gutter / 3,
|
||||
- 'lg': $grid-gutter * 2/3,
|
||||
- 'xl': $grid-gutter
|
||||
- ),
|
||||
- $grid-gutters
|
||||
+ (
|
||||
+ 'xs': $grid-gutter / 12,
|
||||
+ 'sm': $grid-gutter / 6,
|
||||
+ 'md': $grid-gutter / 3,
|
||||
+ 'lg': $grid-gutter * 2/3,
|
||||
+ 'xl': $grid-gutter
|
||||
+ ),
|
||||
+ $grid-gutters
|
||||
);
|
||||
|
||||
$container-max-widths: () !default;
|
||||
$container-max-widths: map-deep-merge(
|
||||
- (
|
||||
- 'md': map-get($grid-breakpoints, 'md') * 0.9375,
|
||||
- 'lg': map-get($grid-breakpoints, 'lg') * 0.9375,
|
||||
- 'xl': map-get($grid-breakpoints, 'xl') * 0.9375
|
||||
- ),
|
||||
- $container-max-widths
|
||||
+ (
|
||||
+ 'md': map-get($grid-breakpoints, 'md') * 0.9375,
|
||||
+ 'lg': map-get($grid-breakpoints, 'lg') * 0.9375,
|
||||
+ 'xl': map-get($grid-breakpoints, 'xl') * 0.9375
|
||||
+ ),
|
||||
+ $container-max-widths
|
||||
);
|
||||
|
||||
$display-breakpoints: () !default;
|
||||
$display-breakpoints: map-deep-merge(
|
||||
- (
|
||||
- 'print-only': 'only print',
|
||||
- 'screen-only': 'only screen',
|
||||
- 'xs-only': 'only screen and (max-width: #{map-get($grid-breakpoints, 'sm') - 1})',
|
||||
- 'sm-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'sm')}) and (max-width: #{map-get($grid-breakpoints, 'md') - 1})',
|
||||
- 'sm-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'md') - 1})',
|
||||
- 'sm-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'sm')})',
|
||||
- 'md-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'md')}) and (max-width: #{map-get($grid-breakpoints, 'lg') - 1})',
|
||||
- 'md-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'lg') - 1})',
|
||||
- 'md-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'md')})',
|
||||
- 'lg-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'lg')}) and (max-width: #{map-get($grid-breakpoints, 'xl') - 1})',
|
||||
- 'lg-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'xl') - 1})',
|
||||
- 'lg-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'lg')})',
|
||||
- 'xl-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'xl')})'
|
||||
- ),
|
||||
- $display-breakpoints
|
||||
+ (
|
||||
+ 'print-only': 'only print',
|
||||
+ 'screen-only': 'only screen',
|
||||
+ 'xs-only': 'only screen and (max-width: #{map-get($grid-breakpoints, 'sm') - 1})',
|
||||
+ 'sm-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'sm')}) and (max-width: #{map-get($grid-breakpoints, 'md') - 1})',
|
||||
+ 'sm-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'md') - 1})',
|
||||
+ 'sm-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'sm')})',
|
||||
+ 'md-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'md')}) and (max-width: #{map-get($grid-breakpoints, 'lg') - 1})',
|
||||
+ 'md-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'lg') - 1})',
|
||||
+ 'md-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'md')})',
|
||||
+ 'lg-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'lg')}) and (max-width: #{map-get($grid-breakpoints, 'xl') - 1})',
|
||||
+ 'lg-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'xl') - 1})',
|
||||
+ 'lg-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'lg')})',
|
||||
+ 'xl-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'xl')})'
|
||||
+ ),
|
||||
+ $display-breakpoints
|
||||
);
|
||||
|
||||
$font-weights: () !default;
|
||||
$font-weights: map-deep-merge(
|
||||
- (
|
||||
- 'thin': 100,
|
||||
- 'light': 300,
|
||||
- 'regular': 400,
|
||||
- 'medium': 500,
|
||||
- 'bold': 700,
|
||||
- 'black': 900
|
||||
- ),
|
||||
- $font-weights
|
||||
+ (
|
||||
+ 'thin': 100,
|
||||
+ 'light': 300,
|
||||
+ 'regular': 400,
|
||||
+ 'medium': 500,
|
||||
+ 'bold': 700,
|
||||
+ 'black': 900
|
||||
+ ),
|
||||
+ $font-weights
|
||||
);
|
||||
|
||||
$heading-font-family: $body-font-family !default;
|
||||
|
||||
$headings: () !default;
|
||||
$headings: map-deep-merge(
|
||||
- (
|
||||
- 'h1': (
|
||||
- 'size': 6rem,
|
||||
- 'weight': 300,
|
||||
- 'line-height': 6rem,
|
||||
- 'letter-spacing': -.015625em,
|
||||
- 'font-family': $heading-font-family,
|
||||
- 'text-transform': false
|
||||
- ),
|
||||
- 'h2': (
|
||||
- 'size': 3.75rem,
|
||||
- 'weight': 300,
|
||||
- 'line-height': 3.75rem,
|
||||
- 'letter-spacing': -.0083333333em,
|
||||
- 'font-family': $heading-font-family,
|
||||
- 'text-transform': false
|
||||
- ),
|
||||
- 'h3': (
|
||||
- 'size': 3rem,
|
||||
- 'weight': 400,
|
||||
- 'line-height': 3.125rem,
|
||||
- 'letter-spacing': normal,
|
||||
- 'font-family': $heading-font-family,
|
||||
- 'text-transform': false
|
||||
- ),
|
||||
- 'h4': (
|
||||
- 'size': 2.125rem,
|
||||
- 'weight': 400,
|
||||
- 'line-height': 2.5rem,
|
||||
- 'letter-spacing': .0073529412em,
|
||||
- 'font-family': $heading-font-family,
|
||||
- 'text-transform': false
|
||||
- ),
|
||||
- 'h5': (
|
||||
- 'size': 1.5rem,
|
||||
- 'weight': 400,
|
||||
- 'line-height': 2rem,
|
||||
- 'letter-spacing': normal,
|
||||
- 'font-family': $heading-font-family,
|
||||
- 'text-transform': false
|
||||
- ),
|
||||
- 'h6': (
|
||||
- 'size': 1.25rem,
|
||||
- 'weight': 500,
|
||||
- 'line-height': 2rem,
|
||||
- 'letter-spacing': .0125em,
|
||||
- 'font-family': $heading-font-family,
|
||||
- 'text-transform': false
|
||||
- ),
|
||||
- 'subtitle-1': (
|
||||
- 'size': 1rem,
|
||||
- 'weight': normal,
|
||||
- 'line-height': 1.75rem,
|
||||
- 'letter-spacing': .009375em,
|
||||
- 'font-family': $body-font-family,
|
||||
- 'text-transform': false
|
||||
- ),
|
||||
- 'subtitle-2': (
|
||||
- 'size': .875rem,
|
||||
- 'weight': 500,
|
||||
- 'line-height': 1.375rem,
|
||||
- 'letter-spacing': .0071428571em,
|
||||
- 'font-family': $body-font-family,
|
||||
- 'text-transform': false
|
||||
- ),
|
||||
- 'body-1': (
|
||||
- 'size': 1rem,
|
||||
- 'weight': 400,
|
||||
- 'line-height': 1.5rem,
|
||||
- 'letter-spacing': .03125em,
|
||||
- 'font-family': $body-font-family,
|
||||
- 'text-transform': false
|
||||
- ),
|
||||
- 'body-2': (
|
||||
- 'size': .875rem,
|
||||
- 'weight': 400,
|
||||
- 'line-height': 1.25rem,
|
||||
- 'letter-spacing': .0178571429em,
|
||||
- 'font-family': $body-font-family,
|
||||
- 'text-transform': false
|
||||
- ),
|
||||
- 'button': (
|
||||
- 'size': .875rem,
|
||||
- 'weight': 500,
|
||||
- 'line-height': 2.25rem,
|
||||
- 'letter-spacing': .0892857143em,
|
||||
- 'font-family': $body-font-family,
|
||||
- 'text-transform': uppercase
|
||||
- ),
|
||||
- 'caption': (
|
||||
- 'size': .75rem,
|
||||
- 'weight': 400,
|
||||
- 'line-height': 1.25rem,
|
||||
- 'letter-spacing': .0333333333em,
|
||||
- 'font-family': $body-font-family,
|
||||
- 'text-transform': false
|
||||
- ),
|
||||
- 'overline': (
|
||||
- 'size': .75rem,
|
||||
- 'weight': 500,
|
||||
- 'line-height': 2rem,
|
||||
- 'letter-spacing': .1666666667em,
|
||||
- 'font-family': $body-font-family,
|
||||
- 'text-transform': uppercase
|
||||
- )
|
||||
- ),
|
||||
- $headings
|
||||
+ (
|
||||
+ 'h1': (
|
||||
+ 'size': 6rem,
|
||||
+ 'weight': 300,
|
||||
+ 'line-height': 6rem,
|
||||
+ 'letter-spacing': -.015625em,
|
||||
+ 'font-family': $heading-font-family,
|
||||
+ 'text-transform': false
|
||||
+ ),
|
||||
+ 'h2': (
|
||||
+ 'size': 3.75rem,
|
||||
+ 'weight': 300,
|
||||
+ 'line-height': 3.75rem,
|
||||
+ 'letter-spacing': -.0083333333em,
|
||||
+ 'font-family': $heading-font-family,
|
||||
+ 'text-transform': false
|
||||
+ ),
|
||||
+ 'h3': (
|
||||
+ 'size': 3rem,
|
||||
+ 'weight': 400,
|
||||
+ 'line-height': 3.125rem,
|
||||
+ 'letter-spacing': normal,
|
||||
+ 'font-family': $heading-font-family,
|
||||
+ 'text-transform': false
|
||||
+ ),
|
||||
+ 'h4': (
|
||||
+ 'size': 2.125rem,
|
||||
+ 'weight': 400,
|
||||
+ 'line-height': 2.5rem,
|
||||
+ 'letter-spacing': .0073529412em,
|
||||
+ 'font-family': $heading-font-family,
|
||||
+ 'text-transform': false
|
||||
+ ),
|
||||
+ 'h5': (
|
||||
+ 'size': 1.5rem,
|
||||
+ 'weight': 400,
|
||||
+ 'line-height': 2rem,
|
||||
+ 'letter-spacing': normal,
|
||||
+ 'font-family': $heading-font-family,
|
||||
+ 'text-transform': false
|
||||
+ ),
|
||||
+ 'h6': (
|
||||
+ 'size': 1.25rem,
|
||||
+ 'weight': 500,
|
||||
+ 'line-height': 2rem,
|
||||
+ 'letter-spacing': .0125em,
|
||||
+ 'font-family': $heading-font-family,
|
||||
+ 'text-transform': false
|
||||
+ ),
|
||||
+ 'subtitle-1': (
|
||||
+ 'size': 1rem,
|
||||
+ 'weight': normal,
|
||||
+ 'line-height': 1.75rem,
|
||||
+ 'letter-spacing': .009375em,
|
||||
+ 'font-family': $body-font-family,
|
||||
+ 'text-transform': false
|
||||
+ ),
|
||||
+ 'subtitle-2': (
|
||||
+ 'size': .875rem,
|
||||
+ 'weight': 500,
|
||||
+ 'line-height': 1.375rem,
|
||||
+ 'letter-spacing': .0071428571em,
|
||||
+ 'font-family': $body-font-family,
|
||||
+ 'text-transform': false
|
||||
+ ),
|
||||
+ 'body-1': (
|
||||
+ 'size': 1rem,
|
||||
+ 'weight': 400,
|
||||
+ 'line-height': 1.5rem,
|
||||
+ 'letter-spacing': .03125em,
|
||||
+ 'font-family': $body-font-family,
|
||||
+ 'text-transform': false
|
||||
+ ),
|
||||
+ 'body-2': (
|
||||
+ 'size': .875rem,
|
||||
+ 'weight': 400,
|
||||
+ 'line-height': 1.25rem,
|
||||
+ 'letter-spacing': .0178571429em,
|
||||
+ 'font-family': $body-font-family,
|
||||
+ 'text-transform': false
|
||||
+ ),
|
||||
+ 'button': (
|
||||
+ 'size': .875rem,
|
||||
+ 'weight': 500,
|
||||
+ 'line-height': 2.25rem,
|
||||
+ 'letter-spacing': .0892857143em,
|
||||
+ 'font-family': $body-font-family,
|
||||
+ 'text-transform': uppercase
|
||||
+ ),
|
||||
+ 'caption': (
|
||||
+ 'size': .75rem,
|
||||
+ 'weight': 400,
|
||||
+ 'line-height': 1.25rem,
|
||||
+ 'letter-spacing': .0333333333em,
|
||||
+ 'font-family': $body-font-family,
|
||||
+ 'text-transform': false
|
||||
+ ),
|
||||
+ 'overline': (
|
||||
+ 'size': .75rem,
|
||||
+ 'weight': 500,
|
||||
+ 'line-height': 2rem,
|
||||
+ 'letter-spacing': .1666666667em,
|
||||
+ 'font-family': $body-font-family,
|
||||
+ 'text-transform': uppercase
|
||||
+ )
|
||||
+ ),
|
||||
+ $headings
|
||||
);
|
||||
|
||||
$typography: () !default;
|
||||
@each $type, $values in $headings {
|
||||
$typography: map-deep-merge(
|
||||
- $typography,
|
||||
- (#{$type}: map-values($values))
|
||||
+ $typography,
|
||||
+ (#{$type}: map-values($values))
|
||||
);
|
||||
}
|
||||
|
||||
$transition: () !default;
|
||||
$transition: map-deep-merge(
|
||||
- (
|
||||
- 'fast-out-slow-in': cubic-bezier(0.4, 0, 0.2, 1),
|
||||
- 'linear-out-slow-in': cubic-bezier(0, 0, 0.2, 1),
|
||||
- 'fast-out-linear-in': cubic-bezier(0.4, 0, 1, 1),
|
||||
- 'ease-in-out': cubic-bezier(0.4, 0, 0.6, 1),
|
||||
- 'fast-in-fast-out': cubic-bezier(0.25, 0.8, 0.25, 1),
|
||||
- 'swing': cubic-bezier(0.25, 0.8, 0.5, 1)
|
||||
- ),
|
||||
- $transition
|
||||
+ (
|
||||
+ 'fast-out-slow-in': cubic-bezier(0.4, 0, 0.2, 1),
|
||||
+ 'linear-out-slow-in': cubic-bezier(0, 0, 0.2, 1),
|
||||
+ 'fast-out-linear-in': cubic-bezier(0.4, 0, 1, 1),
|
||||
+ 'ease-in-out': cubic-bezier(0.4, 0, 0.6, 1),
|
||||
+ 'fast-in-fast-out': cubic-bezier(0.25, 0.8, 0.25, 1),
|
||||
+ 'swing': cubic-bezier(0.25, 0.8, 0.5, 1)
|
||||
+ ),
|
||||
+ $transition
|
||||
);
|
||||
$primary-transition: 0.3s map-get($transition, 'swing') !default;
|
||||
$secondary-transition: 0.2s map-get($transition, 'ease-in-out') !default;
|
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 161 KiB |
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 226 KiB |
Before Width: | Height: | Size: 697 KiB After Width: | Height: | Size: 697 KiB |
Before Width: | Height: | Size: 496 KiB After Width: | Height: | Size: 496 KiB |
|
@ -8,7 +8,7 @@
|
|||
<g>
|
||||
<path d="M165.4,122l-50,49.9c-0.2,0.2-0.5,0.3-0.7,0.2l-68.3-18.3c-0.3-0.1-0.5-0.3-0.5-0.5L27.5,85.1c-0.1-0.3,0-0.5,0.2-0.7
|
||||
l50-49.9c0.2-0.2,0.5-0.3,0.7-0.2l68.3,18.3c0.3,0.1,0.5,0.3,0.5,0.5l18.3,68.2C165.7,121.6,165.6,121.8,165.4,122z M98.4,67.7
|
||||
L31.3,85.6c-0.1,0-0.2,0.2-0.1,0.3l49.1,49c0.1,0.1,0.3,0.1,0.3-0.1l18-67C98.7,67.8,98.5,67.6,98.4,67.7z"/>
|
||||
L31.3,85.6c-0.1,0-0.2,0.2-0.1,0.3l49.1,49c0.1,0.1,0.3,0.1,0.3-0.1l18-67C98.7,67.8,98.5,67.6,98.4,67.7z" fill="white"/>
|
||||
<g>
|
||||
<rect class="st0" width="193.2" height="206.7"/>
|
||||
</g>
|
Before Width: | Height: | Size: 843 B After Width: | Height: | Size: 856 B |
BIN
public/images/flowforms.png
Normal file
After Width: | Height: | Size: 572 KiB |
BIN
public/images/flowinity-android.png
Normal file
After Width: | Height: | Size: 489 KiB |
BIN
public/images/flowinity-update-comms.png
Normal file
After Width: | Height: | Size: 3.1 MiB |
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
Before Width: | Height: | Size: 2 MiB After Width: | Height: | Size: 2 MiB |
BIN
public/images/kansas.png
Normal file
After Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 273 KiB After Width: | Height: | Size: 273 KiB |
BIN
public/images/mira.png
Normal file
After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 328 KiB After Width: | Height: | Size: 328 KiB |
BIN
public/images/proj01.png
Normal file
After Width: | Height: | Size: 488 KiB |
Before Width: | Height: | Size: 2.9 MiB After Width: | Height: | Size: 2.9 MiB |
Before Width: | Height: | Size: 571 KiB After Width: | Height: | Size: 571 KiB |
Before Width: | Height: | Size: 258 KiB After Width: | Height: | Size: 258 KiB |
|
@ -1,27 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>Troplo's Website</title>
|
||||
<meta name="title" content="Troplo's Website">
|
||||
<meta name="keywords" content="troplo, flowinity, pinnoto, berri, mira, polytoria, vixlatio, jays.host, troplo-site">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="language" content="English">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but Troplo's Website doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
21
src/App.vue
|
@ -1,27 +1,12 @@
|
|||
<template>
|
||||
<v-app>
|
||||
<v-main>
|
||||
<Header></Header>
|
||||
<Header />
|
||||
<router-view />
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Header from "./components/Header.vue"
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
Header
|
||||
},
|
||||
data: () => ({
|
||||
//
|
||||
}),
|
||||
watch: {
|
||||
$route(to) {
|
||||
this.$store.commit("setRoute", to.name)
|
||||
document.title = to.name + " - " + this.$store.state.site.name
|
||||
}
|
||||
}
|
||||
}
|
||||
<script lang="ts" setup>
|
||||
import Header from "@/components/Header.vue"
|
||||
</script>
|
||||
|
|
Before Width: | Height: | Size: 502 KiB |
BIN
src/assets/logo.png
Normal file
After Width: | Height: | Size: 12 KiB |
6
src/assets/logo.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M261.126 140.65L164.624 307.732L256.001 466L377.028 256.5L498.001 47H315.192L261.126 140.65Z" fill="#1697F6"/>
|
||||
<path d="M135.027 256.5L141.365 267.518L231.64 111.178L268.731 47H256H14L135.027 256.5Z" fill="#AEDDFF"/>
|
||||
<path d="M315.191 47C360.935 197.446 256 466 256 466L164.624 307.732L315.191 47Z" fill="#1867C0"/>
|
||||
<path d="M268.731 47C76.0026 47 141.366 267.518 141.366 267.518L268.731 47Z" fill="#7BC6FF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 526 B |
31
src/components/ChipTag.vue
Normal file
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<v-chip
|
||||
:href="tag.link"
|
||||
:color="tag.color"
|
||||
:disabled="!tag.link"
|
||||
style="opacity: 1"
|
||||
>
|
||||
<v-img
|
||||
v-if="tag.icon === 'crystal'"
|
||||
src="/images/crystal-icon.svg"
|
||||
width="26"
|
||||
class="mx-n1"
|
||||
></v-img>
|
||||
<v-icon v-if="tag.icon !== 'crystal'">{{ tag.icon }}</v-icon>
|
||||
<template v-if="tag.icon"> </template>
|
||||
{{ tag.name }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
tag: {
|
||||
name: string
|
||||
link?: string
|
||||
color?: string
|
||||
icon?: string
|
||||
}
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
16
src/components/DiscordLogo.vue
Normal file
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36">
|
||||
<path
|
||||
:fill="theme.current.value.dark ? 'white' : 'black'"
|
||||
d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTheme } from "vuetify"
|
||||
|
||||
const theme = useTheme()
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
18
src/components/FlowinityLogo.vue
Normal file
|
@ -0,0 +1,18 @@
|
|||
<template>
|
||||
<svg viewBox="0 0 902 902" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M418.455 0.303628C368.601 3.53963 317.099 15.4736 274.671 33.6216C247.274 45.3406 230.542 54.3696 204.955 71.2386C176.877 89.7506 157.205 105.979 130.432 132.715C92.391 170.705 65.015 208.32 43.462 252.215C24.648 290.532 12.494 328.601 5.50901 371.098C0.525014 401.415 -0.0489904 409.979 0.00300957 453.215C0.0460096 488.587 0.307011 495.159 2.26401 510.012C12.064 584.424 36.235 647.831 76.868 705.715C88.227 721.897 108.825 746.978 121.86 760.5V760.5C123.716 762.425 126.973 761.118 126.983 758.444L127.455 635.5L127.933 510.88C127.948 507.16 128.87 503.498 130.62 500.215V500.215C134.346 493.224 140.794 486.918 147.366 483.836C152.023 481.652 154.372 481.215 161.455 481.215C168.538 481.215 170.887 481.652 175.544 483.836C182.369 487.037 188.65 493.382 192.253 500.715V500.715C194.031 504.333 194.959 508.311 194.965 512.342L195.22 665.376L195.464 811.774C195.477 819.73 199.528 827.136 206.22 831.44V831.44C219.113 839.731 233.998 848.715 234.844 848.715C235.18 848.715 235.455 724.137 235.455 571.875C235.455 301.89 235.503 294.849 237.38 287.507C243.152 264.94 260.408 246.135 281.881 239.012C289.036 236.639 291.492 236.334 303.455 236.334C315.418 236.334 317.874 236.639 325.029 239.012C346.502 246.135 363.758 264.94 369.53 287.507C371.409 294.855 371.455 302.194 371.455 595.356V893.375C371.455 894.718 372.392 895.879 373.705 896.164V896.164C376.489 896.767 387.479 898.218 401.955 899.893C414.535 901.348 479.439 901.702 491.685 900.382V900.382C496.084 899.907 499.42 896.197 499.426 891.773L499.685 708.382L499.943 525.666C499.951 520.069 500.875 514.512 502.679 509.215V509.215C506.467 498.094 511.754 489.557 519.98 481.282C527.75 473.464 535.038 468.745 545.213 464.94C553.437 461.865 572.642 460.71 582.746 462.683C605.287 467.084 624.838 484.756 633.131 508.225V508.225C635 513.513 635.963 519.08 635.979 524.689L636.455 689.875L636.913 848.991C636.932 855.678 643.978 860.012 649.955 857.013V857.013C664.911 849.51 682.159 839.593 695.901 830.595V830.595C701.863 826.692 705.455 820.046 705.455 812.92V583.211C705.455 413.319 705.777 340.339 706.546 336.18C708.763 324.185 716.558 313.757 727.23 308.507C733.573 305.387 734.5 305.215 744.939 305.219C755.049 305.222 758.649 305.954 763.5 308.507C774.055 314.061 779.116 322.479 781.905 333.185C783.281 338.467 783.455 362.492 783.455 547.048C783.455 741.465 783.564 754.871 785.131 753.587C786.054 752.833 791.159 746.998 796.477 740.621C856.831 668.25 888.647 594.484 899.156 502.556C901.658 480.679 901.662 423.739 899.164 401.215C893.347 348.771 882.256 306.938 862.869 264.33C824.083 179.088 751.55 100.867 667.955 54.1346C618.093 26.2596 566.07 9.88263 502.955 2.19063C490.626 0.687629 432.526 -0.609372 418.455 0.303628Z"
|
||||
:fill="theme.current.value.dark ? 'white' : 'black'"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTheme } from "vuetify"
|
||||
|
||||
const theme = useTheme()
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
33
src/components/FlowinityWordmark.vue
Normal file
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<svg
|
||||
width="2042"
|
||||
height="512"
|
||||
viewBox="0 0 2042 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:fill="theme.current.value.dark ? '#fff' : '#121212'"
|
||||
>
|
||||
<g clip-path="url(#clip0_319_20)">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M512 256C512 321.439 487.446 381.144 447.049 426.404V196.232C447.049 184.021 437.15 174.121 424.939 174.121C412.727 174.121 402.828 184.021 402.828 196.232V465.735C390.664 474.266 377.724 481.766 364.134 488.108V306.785C364.134 285.034 346.501 267.401 324.75 267.401C302.998 267.401 285.365 285.034 285.365 306.785V510.334C275.729 511.435 265.931 512 256 512C241.764 512 227.8 510.838 214.197 508.604V174.121C214.197 152.37 196.564 134.737 174.812 134.737C153.061 134.737 135.428 152.37 135.428 174.122V481.885C128.31 478.078 121.394 473.945 114.699 469.505V294.348C114.699 283.282 105.727 274.31 94.6608 274.31C83.5943 274.31 74.623 283.282 74.623 294.348V436.661C28.5064 390.363 0 326.51 0 256C0 114.615 114.615 0 256 0C397.385 0 512 114.615 512 256Z"
|
||||
/>
|
||||
<path
|
||||
d="M587.818 419V139.727H755.273V169.727H621.636V264.091H742.727V294.091H621.636V419H587.818ZM825.651 139.727V419H793.47V139.727H825.651ZM954.325 423.364C935.416 423.364 918.825 418.864 904.553 409.864C890.371 400.864 879.28 388.273 871.28 372.091C863.371 355.909 859.416 337 859.416 315.364C859.416 293.545 863.371 274.5 871.28 258.227C879.28 241.955 890.371 229.318 904.553 220.318C918.825 211.318 935.416 206.818 954.325 206.818C973.235 206.818 989.78 211.318 1003.96 220.318C1018.23 229.318 1029.33 241.955 1037.23 258.227C1045.23 274.5 1049.23 293.545 1049.23 315.364C1049.23 337 1045.23 355.909 1037.23 372.091C1029.33 388.273 1018.23 400.864 1003.96 409.864C989.78 418.864 973.235 423.364 954.325 423.364ZM954.325 394.455C968.689 394.455 980.507 390.773 989.78 383.409C999.053 376.045 1005.92 366.364 1010.37 354.364C1014.83 342.364 1017.05 329.364 1017.05 315.364C1017.05 301.364 1014.83 288.318 1010.37 276.227C1005.92 264.136 999.053 254.364 989.78 246.909C980.507 239.455 968.689 235.727 954.325 235.727C939.962 235.727 928.144 239.455 918.871 246.909C909.598 254.364 902.735 264.136 898.28 276.227C893.825 288.318 891.598 301.364 891.598 315.364C891.598 329.364 893.825 342.364 898.28 354.364C902.735 366.364 909.598 376.045 918.871 383.409C928.144 390.773 939.962 394.455 954.325 394.455ZM1123.33 419L1059.51 209.545H1093.33L1138.6 369.909H1140.78L1185.51 209.545H1219.87L1264.06 369.364H1266.24L1311.51 209.545H1345.33L1281.51 419H1249.87L1204.06 258.091H1200.78L1154.97 419H1123.33ZM1372.51 419V209.545H1404.7V419H1372.51ZM1388.88 174.636C1382.61 174.636 1377.2 172.5 1372.65 168.227C1368.2 163.955 1365.97 158.818 1365.97 152.818C1365.97 146.818 1368.2 141.682 1372.65 137.409C1377.2 133.136 1382.61 131 1388.88 131C1395.15 131 1400.51 133.136 1404.97 137.409C1409.51 141.682 1411.79 146.818 1411.79 152.818C1411.79 158.818 1409.51 163.955 1404.97 168.227C1400.51 172.5 1395.15 174.636 1388.88 174.636ZM1480.46 293V419H1448.28V209.545H1479.37V242.273H1482.1C1487.01 231.636 1494.46 223.091 1504.46 216.636C1514.46 210.091 1527.37 206.818 1543.19 206.818C1557.37 206.818 1569.78 209.727 1580.42 215.545C1591.05 221.273 1599.33 230 1605.23 241.727C1611.14 253.364 1614.1 268.091 1614.1 285.909V419H1581.92V288.091C1581.92 271.636 1577.64 258.818 1569.1 249.636C1560.55 240.364 1548.83 235.727 1533.92 235.727C1523.64 235.727 1514.46 237.955 1506.37 242.409C1498.37 246.864 1492.05 253.364 1487.42 261.909C1482.78 270.455 1480.46 280.818 1480.46 293ZM1657.54 419V209.545H1689.73V419H1657.54ZM1673.91 174.636C1667.64 174.636 1662.23 172.5 1657.68 168.227C1653.23 163.955 1651 158.818 1651 152.818C1651 146.818 1653.23 141.682 1657.68 137.409C1662.23 133.136 1667.64 131 1673.91 131C1680.18 131 1685.54 133.136 1690 137.409C1694.54 141.682 1696.82 146.818 1696.82 152.818C1696.82 158.818 1694.54 163.955 1690 168.227C1685.54 172.5 1680.18 174.636 1673.91 174.636ZM1824.4 209.545V236.818H1715.85V209.545H1824.4ZM1747.49 159.364H1779.67V359C1779.67 368.091 1780.99 374.909 1783.63 379.455C1786.35 383.909 1789.81 386.909 1793.99 388.455C1798.26 389.909 1802.76 390.636 1807.49 390.636C1811.04 390.636 1813.95 390.455 1816.22 390.091C1818.49 389.636 1820.31 389.273 1821.67 389L1828.22 417.909C1826.04 418.727 1822.99 419.545 1819.08 420.364C1815.17 421.273 1810.22 421.727 1804.22 421.727C1795.13 421.727 1786.22 419.773 1777.49 415.864C1768.85 411.955 1761.67 406 1755.95 398C1750.31 390 1747.49 379.909 1747.49 367.727V159.364ZM1878.18 497.545C1872.72 497.545 1867.86 497.091 1863.59 496.182C1859.31 495.364 1856.36 494.545 1854.72 493.727L1862.9 465.364C1870.72 467.364 1877.63 468.091 1883.63 467.545C1889.63 467 1894.95 464.318 1899.59 459.5C1904.31 454.773 1908.63 447.091 1912.54 436.455L1918.54 420.091L1841.09 209.545H1875.99L1933.81 376.455H1935.99L1993.81 209.545H2028.72L1939.81 449.545C1935.81 460.364 1930.86 469.318 1924.95 476.409C1919.04 483.591 1912.18 488.909 1904.36 492.364C1896.63 495.818 1887.9 497.545 1878.18 497.545Z"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_319_20">
|
||||
<rect width="2042" height="512" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTheme } from "vuetify"
|
||||
|
||||
const theme = useTheme()
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,94 +1,129 @@
|
|||
<template>
|
||||
<div id="header">
|
||||
<v-toolbar dark>
|
||||
<v-app-bar
|
||||
color="surface"
|
||||
:extension-height="announcementsStore.navbarOffset - 64"
|
||||
>
|
||||
<template #extension>
|
||||
<div class="d-flex flex-column w-100">
|
||||
<v-alert
|
||||
v-for="banner in announcementsStore.banners"
|
||||
:key="banner.id"
|
||||
:value="true"
|
||||
variant="tonal"
|
||||
:type="banner.bannerType"
|
||||
:icon="
|
||||
banner.bannerType === 'error' ? 'mdi-alert-circle' : undefined
|
||||
"
|
||||
tile
|
||||
:id="`banner-${banner.id}`"
|
||||
>
|
||||
<strong v-if="banner.id !== 'status'">UPCOMING:</strong>
|
||||
{{ banner.bannerText }}
|
||||
<template #append>
|
||||
<v-btn
|
||||
:to="`/news/${banner.id}`"
|
||||
size="small"
|
||||
variant="outlined"
|
||||
v-if="banner.id !== 'status'"
|
||||
>Learn more</v-btn
|
||||
>
|
||||
<v-btn
|
||||
v-else
|
||||
size="small"
|
||||
variant="outlined"
|
||||
href="https://status.troplo.com"
|
||||
target="_blank"
|
||||
>Learn More</v-btn
|
||||
>
|
||||
</template>
|
||||
</v-alert>
|
||||
</div>
|
||||
</template>
|
||||
<v-app-bar-nav-icon
|
||||
@click.stop="sidebar = !sidebar"
|
||||
v-if="$vuetify.breakpoint.mobile"
|
||||
v-if="display.mobile.value"
|
||||
></v-app-bar-nav-icon>
|
||||
<v-toolbar-title
|
||||
class="troplo-title"
|
||||
@click="$router.push('/')"
|
||||
@click="router.push('/')"
|
||||
style="cursor: pointer"
|
||||
>Troplo's Website</v-toolbar-title
|
||||
>
|
||||
<v-spacer></v-spacer>
|
||||
<v-list v-if="!$vuetify.breakpoint.mobile">
|
||||
<div class="d-flex mr-4" style="gap: 4px" v-if="!display.mobile.value">
|
||||
<v-btn
|
||||
text
|
||||
variant="text"
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
class="ml-1"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
style="text-transform: unset !important"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="false"
|
||||
class="ml-1"
|
||||
text
|
||||
@click="$vuetify.theme.dark = !$vuetify.theme.dark"
|
||||
>
|
||||
<v-list-item-title>
|
||||
<v-icon>{{
|
||||
$vuetify.theme.dark ? "mdi-lightbulb-on" : "mdi-lightbulb-outline"
|
||||
}}</v-icon>
|
||||
</v-list-item-title>
|
||||
</v-btn>
|
||||
</v-list>
|
||||
</v-toolbar>
|
||||
</div>
|
||||
</v-app-bar>
|
||||
<v-navigation-drawer
|
||||
floating
|
||||
color="dark"
|
||||
app
|
||||
v-model="sidebar"
|
||||
v-if="$vuetify.breakpoint.mobile"
|
||||
v-if="display.mobile.value"
|
||||
expand
|
||||
>
|
||||
<v-divider></v-divider>
|
||||
<v-list nav dense>
|
||||
<v-list-item v-for="item in items" :key="item.id" link :to="item.path">
|
||||
<v-list-item-icon>
|
||||
<v-icon>{{ item.icon }}</v-icon>
|
||||
</v-list-item-icon>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
v-for="item in items"
|
||||
:key="item.path"
|
||||
link
|
||||
@click="$vuetify.theme.dark = !$vuetify.theme.dark"
|
||||
v-if="false"
|
||||
:to="item.path"
|
||||
>
|
||||
<v-list-item-icon>
|
||||
<v-icon>{{
|
||||
$vuetify.theme.dark ? "mdi-lightbulb-on" : "mdi-lightbulb-outline"
|
||||
}}</v-icon>
|
||||
</v-list-item-icon>
|
||||
<template #prepend>
|
||||
<v-icon>{{ item.icon }}</v-icon>
|
||||
</template>
|
||||
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Header",
|
||||
data() {
|
||||
return {
|
||||
sidebar: false,
|
||||
items: [
|
||||
{ id: 1, title: "Home", icon: "mdi-home", path: "/" },
|
||||
{ id: 3, title: "Contact", icon: "mdi-email", path: "/contact" }
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$vuetify.theme = { dark: true }
|
||||
<script setup lang="ts">
|
||||
import { useDisplay } from "vuetify"
|
||||
import { onMounted, ref } from "vue"
|
||||
import { useAnnouncementsStore } from "@/stores/announcements.store"
|
||||
import { useRouter } from "vue-router"
|
||||
|
||||
const display = useDisplay()
|
||||
const announcementsStore = useAnnouncementsStore()
|
||||
|
||||
const sidebar = ref(false)
|
||||
const items = ref<
|
||||
{
|
||||
title: string
|
||||
icon: string
|
||||
path: string
|
||||
disabled?: boolean
|
||||
}[]
|
||||
>([
|
||||
{ title: "Home", icon: "mdi-home", path: "/" },
|
||||
{ title: "Contact", icon: "mdi-email", path: "/contact" },
|
||||
{
|
||||
title: "Blog & Updates",
|
||||
icon: "mdi-newspaper",
|
||||
path: "/news"
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
onMounted(async () => {
|
||||
announcementsStore.banners = await announcementsStore.getAnnouncements({
|
||||
banner: true
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
35
src/components/README.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Components
|
||||
|
||||
Vue template files in this folder are automatically imported.
|
||||
|
||||
## 🚀 Usage
|
||||
|
||||
Importing is handled by [unplugin-vue-components](https://github.com/unplugin/unplugin-vue-components). This plugin automatically imports `.vue` files created in the `src/components` directory, and registers them as global components. This means that you can use any component in your application without having to manually import it.
|
||||
|
||||
The following example assumes a component located at `src/components/MyComponent.vue`:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<MyComponent />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
//
|
||||
</script>
|
||||
```
|
||||
|
||||
When your template is rendered, the component's import will automatically be inlined, which renders to this:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<MyComponent />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import MyComponent from '@/components/MyComponent.vue'
|
||||
</script>
|
||||
```
|
87
src/gql/fragment-masking.ts
Normal file
|
@ -0,0 +1,87 @@
|
|||
/* eslint-disable */
|
||||
import type { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
|
||||
import type { FragmentDefinitionNode } from 'graphql';
|
||||
import type { Incremental } from './graphql';
|
||||
|
||||
|
||||
export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = TDocumentType extends DocumentTypeDecoration<
|
||||
infer TType,
|
||||
any
|
||||
>
|
||||
? [TType] extends [{ ' $fragmentName'?: infer TKey }]
|
||||
? TKey extends string
|
||||
? { ' $fragmentRefs'?: { [key in TKey]: TType } }
|
||||
: never
|
||||
: never
|
||||
: never;
|
||||
|
||||
// return non-nullable if `fragmentType` is non-nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>>
|
||||
): TType;
|
||||
// return nullable if `fragmentType` is undefined
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | undefined
|
||||
): TType | undefined;
|
||||
// return nullable if `fragmentType` is nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null
|
||||
): TType | null;
|
||||
// return nullable if `fragmentType` is nullable or undefined
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined
|
||||
): TType | null | undefined;
|
||||
// return array of non-nullable if `fragmentType` is array of non-nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>>
|
||||
): Array<TType>;
|
||||
// return array of nullable if `fragmentType` is array of nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
|
||||
): Array<TType> | null | undefined;
|
||||
// return readonly array of non-nullable if `fragmentType` is array of non-nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
|
||||
): ReadonlyArray<TType>;
|
||||
// return readonly array of nullable if `fragmentType` is array of nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
|
||||
): ReadonlyArray<TType> | null | undefined;
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | Array<FragmentType<DocumentTypeDecoration<TType, any>>> | ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
|
||||
): TType | Array<TType> | ReadonlyArray<TType> | null | undefined {
|
||||
return fragmentType as any;
|
||||
}
|
||||
|
||||
|
||||
export function makeFragmentData<
|
||||
F extends DocumentTypeDecoration<any, any>,
|
||||
FT extends ResultOf<F>
|
||||
>(data: FT, _fragment: F): FragmentType<F> {
|
||||
return data as FragmentType<F>;
|
||||
}
|
||||
export function isFragmentReady<TQuery, TFrag>(
|
||||
queryNode: DocumentTypeDecoration<TQuery, any>,
|
||||
fragmentNode: TypedDocumentNode<TFrag>,
|
||||
data: FragmentType<TypedDocumentNode<Incremental<TFrag>, any>> | null | undefined
|
||||
): data is FragmentType<typeof fragmentNode> {
|
||||
const deferredFields = (queryNode as { __meta__?: { deferredFields: Record<string, (keyof TFrag)[]> } }).__meta__
|
||||
?.deferredFields;
|
||||
|
||||
if (!deferredFields) return true;
|
||||
|
||||
const fragDef = fragmentNode.definitions[0] as FragmentDefinitionNode | undefined;
|
||||
const fragName = fragDef?.name?.value;
|
||||
|
||||
const fields = (fragName && deferredFields[fragName]) || [];
|
||||
return fields.length > 0 && fields.every(field => data && field in data);
|
||||
}
|
52
src/gql/gql.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* eslint-disable */
|
||||
import * as types from './graphql';
|
||||
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
||||
|
||||
/**
|
||||
* Map of all GraphQL operations in the project.
|
||||
*
|
||||
* This map has several performance disadvantages:
|
||||
* 1. It is not tree-shakeable, so it will include all operations in the project.
|
||||
* 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle.
|
||||
* 3. It does not support dead code elimination, so it will add unused operations.
|
||||
*
|
||||
* Therefore it is highly recommended to use the babel or swc plugin for production.
|
||||
*/
|
||||
const documents = {
|
||||
"\n query Announcement($announcementId: String!) {\n announcement(id: $announcementId) {\n id\n title\n description\n content\n createdAt\n updatedAt\n bannerExpiry\n image\n flowinityUser {\n username\n avatar\n }\n }\n }\n": types.AnnouncementDocument,
|
||||
"\n query Announcements($input: AnnouncementsInput!) {\n announcements(input: $input) {\n id\n title\n description\n createdAt\n updatedAt\n image\n banner\n bannerText\n bannerExpiry\n bannerType\n flowinityUserId\n }\n }\n": types.AnnouncementsDocument,
|
||||
"\n query StatusPage {\n status {\n name\n status\n }\n }\n": types.StatusPageDocument,
|
||||
};
|
||||
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`);
|
||||
* ```
|
||||
*
|
||||
* The query argument is unknown!
|
||||
* Please regenerate the types.
|
||||
*/
|
||||
export function graphql(source: string): unknown;
|
||||
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query Announcement($announcementId: String!) {\n announcement(id: $announcementId) {\n id\n title\n description\n content\n createdAt\n updatedAt\n bannerExpiry\n image\n flowinityUser {\n username\n avatar\n }\n }\n }\n"): (typeof documents)["\n query Announcement($announcementId: String!) {\n announcement(id: $announcementId) {\n id\n title\n description\n content\n createdAt\n updatedAt\n bannerExpiry\n image\n flowinityUser {\n username\n avatar\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query Announcements($input: AnnouncementsInput!) {\n announcements(input: $input) {\n id\n title\n description\n createdAt\n updatedAt\n image\n banner\n bannerText\n bannerExpiry\n bannerType\n flowinityUserId\n }\n }\n"): (typeof documents)["\n query Announcements($input: AnnouncementsInput!) {\n announcements(input: $input) {\n id\n title\n description\n createdAt\n updatedAt\n image\n banner\n bannerText\n bannerExpiry\n bannerType\n flowinityUserId\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query StatusPage {\n status {\n name\n status\n }\n }\n"): (typeof documents)["\n query StatusPage {\n status {\n name\n status\n }\n }\n"];
|
||||
|
||||
export function graphql(source: string) {
|
||||
return (documents as any)[source] ?? {};
|
||||
}
|
||||
|
||||
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never;
|
113
src/gql/graphql.ts
Normal file
|
@ -0,0 +1,113 @@
|
|||
/* eslint-disable */
|
||||
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
||||
export type Maybe<T> = T | null;
|
||||
export type InputMaybe<T> = Maybe<T>;
|
||||
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
||||
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
|
||||
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
|
||||
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
|
||||
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
|
||||
/** All built-in and custom scalars, mapped to their actual values */
|
||||
export type Scalars = {
|
||||
ID: { input: string; output: string; }
|
||||
String: { input: string; output: string; }
|
||||
Boolean: { input: boolean; output: boolean; }
|
||||
Int: { input: number; output: number; }
|
||||
Float: { input: number; output: number; }
|
||||
Date: { input: any; output: any; }
|
||||
};
|
||||
|
||||
export type Announcement = {
|
||||
__typename?: 'Announcement';
|
||||
banner: Scalars['String']['output'];
|
||||
bannerExpiry?: Maybe<Scalars['Date']['output']>;
|
||||
bannerText?: Maybe<Scalars['String']['output']>;
|
||||
bannerType?: Maybe<BannerType>;
|
||||
content?: Maybe<Scalars['String']['output']>;
|
||||
createdAt: Scalars['Date']['output'];
|
||||
description: Scalars['String']['output'];
|
||||
flowinityUser?: Maybe<FlowinityUser>;
|
||||
flowinityUserId: Scalars['Int']['output'];
|
||||
id: Scalars['String']['output'];
|
||||
/** Resolvable URL to an image */
|
||||
image: Scalars['String']['output'];
|
||||
title: Scalars['String']['output'];
|
||||
updatedAt: Scalars['Date']['output'];
|
||||
};
|
||||
|
||||
export type AnnouncementsInput = {
|
||||
banner?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
/** The page number. Ignored if banner is true. */
|
||||
page?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
||||
/** The type of the banner */
|
||||
export enum BannerType {
|
||||
Error = 'error',
|
||||
Info = 'info',
|
||||
Success = 'success',
|
||||
Warning = 'warning'
|
||||
}
|
||||
|
||||
export type FlowinityUser = {
|
||||
__typename?: 'FlowinityUser';
|
||||
avatar?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['Int']['output'];
|
||||
username: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type Query = {
|
||||
__typename?: 'Query';
|
||||
announcement?: Maybe<Announcement>;
|
||||
announcements: Array<Announcement>;
|
||||
information: Scalars['String']['output'];
|
||||
status: Array<StatusMonitor>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryAnnouncementArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryAnnouncementsArgs = {
|
||||
input: AnnouncementsInput;
|
||||
};
|
||||
|
||||
/** The status of the monitor */
|
||||
export enum Status {
|
||||
Down = 'DOWN',
|
||||
Maintenance = 'MAINTENANCE',
|
||||
Pending = 'PENDING',
|
||||
Up = 'UP'
|
||||
}
|
||||
|
||||
export type StatusMonitor = {
|
||||
__typename?: 'StatusMonitor';
|
||||
name: Scalars['String']['output'];
|
||||
status: Status;
|
||||
};
|
||||
|
||||
export type AnnouncementQueryVariables = Exact<{
|
||||
announcementId: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type AnnouncementQuery = { __typename?: 'Query', announcement?: { __typename?: 'Announcement', id: string, title: string, description: string, content?: string | null, createdAt: any, updatedAt: any, bannerExpiry?: any | null, image: string, flowinityUser?: { __typename?: 'FlowinityUser', username: string, avatar?: string | null } | null } | null };
|
||||
|
||||
export type AnnouncementsQueryVariables = Exact<{
|
||||
input: AnnouncementsInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type AnnouncementsQuery = { __typename?: 'Query', announcements: Array<{ __typename?: 'Announcement', id: string, title: string, description: string, createdAt: any, updatedAt: any, image: string, banner: string, bannerText?: string | null, bannerExpiry?: any | null, bannerType?: BannerType | null, flowinityUserId: number }> };
|
||||
|
||||
export type StatusPageQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type StatusPageQuery = { __typename?: 'Query', status: Array<{ __typename?: 'StatusMonitor', name: string, status: Status }> };
|
||||
|
||||
|
||||
export const AnnouncementDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Announcement"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"announcementId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"announcement"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"announcementId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"bannerExpiry"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"flowinityUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}}]}}]} as unknown as DocumentNode<AnnouncementQuery, AnnouncementQueryVariables>;
|
||||
export const AnnouncementsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Announcements"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AnnouncementsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"announcements"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"banner"}},{"kind":"Field","name":{"kind":"Name","value":"bannerText"}},{"kind":"Field","name":{"kind":"Name","value":"bannerExpiry"}},{"kind":"Field","name":{"kind":"Name","value":"bannerType"}},{"kind":"Field","name":{"kind":"Name","value":"flowinityUserId"}}]}}]}}]} as unknown as DocumentNode<AnnouncementsQuery, AnnouncementsQueryVariables>;
|
||||
export const StatusPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StatusPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]} as unknown as DocumentNode<StatusPageQuery, StatusPageQueryVariables>;
|
2
src/gql/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from "./fragment-masking";
|
||||
export * from "./gql";
|
20
src/graphql/announcements/announcement.query.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import gql from "graphql-tag"
|
||||
|
||||
export const AnnouncementQuery = gql`
|
||||
query Announcement($announcementId: String!) {
|
||||
announcement(id: $announcementId) {
|
||||
id
|
||||
title
|
||||
description
|
||||
content
|
||||
createdAt
|
||||
updatedAt
|
||||
bannerExpiry
|
||||
image
|
||||
flowinityUser {
|
||||
username
|
||||
avatar
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
19
src/graphql/announcements/announcements.query.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import gql from "graphql-tag"
|
||||
|
||||
export const AnnouncementsQuery = gql`
|
||||
query Announcements($input: AnnouncementsInput!) {
|
||||
announcements(input: $input) {
|
||||
id
|
||||
title
|
||||
description
|
||||
createdAt
|
||||
updatedAt
|
||||
image
|
||||
banner
|
||||
bannerText
|
||||
bannerExpiry
|
||||
bannerType
|
||||
flowinityUserId
|
||||
}
|
||||
}
|
||||
`
|
10
src/graphql/status/status.query.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import gql from "graphql-tag"
|
||||
|
||||
export const StatusPageQuery = gql`
|
||||
query StatusPage {
|
||||
status {
|
||||
name
|
||||
status
|
||||
}
|
||||
}
|
||||
`
|
13
src/lib/dayjs.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* plugins/dayjs.ts
|
||||
*/
|
||||
|
||||
import dayjs from "dayjs";
|
||||
import advancedFormat from "dayjs/plugin/advancedFormat";
|
||||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||
|
||||
dayjs.extend(advancedFormat);
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(customParseFormat);
|
||||
export default dayjs;
|
16
src/lib/mdAnnouncements.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import MarkdownIt from "markdown-it"
|
||||
|
||||
const md = new MarkdownIt({
|
||||
html: true,
|
||||
linkify: true,
|
||||
typographer: false,
|
||||
breaks: true,
|
||||
quotes: "“”‘’",
|
||||
langPrefix: "language-",
|
||||
xhtmlOut: true
|
||||
})
|
||||
|
||||
const mdNoHtml = new MarkdownIt("commonmark", {})
|
||||
|
||||
export default md
|
||||
export { mdNoHtml }
|
106
src/main.js
|
@ -1,106 +0,0 @@
|
|||
import Vue from "vue"
|
||||
import App from "./App.vue"
|
||||
import router from "./router"
|
||||
import vuetify from "./plugins/vuetify"
|
||||
import store from "./store"
|
||||
import VueMatomo from "vue-matomo"
|
||||
import "./assets/style.css"
|
||||
Vue.config.productionTip = false
|
||||
Vue.use(VueMatomo, {
|
||||
// Configure your matomo server and site by providing
|
||||
host: "https://analytics.flowinity.com",
|
||||
siteId: 4,
|
||||
|
||||
// Changes the default .js and .php endpoint's filename
|
||||
// Default: 'matomo'
|
||||
trackerFileName: "matomo",
|
||||
|
||||
// Overrides the autogenerated tracker endpoint entirely
|
||||
// Default: undefined
|
||||
// trackerUrl: 'https://example.com/whatever/endpoint/you/have',
|
||||
|
||||
// Overrides the autogenerated tracker script path entirely
|
||||
// Default: undefined
|
||||
// trackerScriptUrl: 'https://example.com/whatever/script/path/you/have',
|
||||
|
||||
// Enables automatically registering pageviews on the router
|
||||
router: router,
|
||||
|
||||
// Enables link tracking on regular links. Note that this won't
|
||||
// work for routing links (ie. internal Vue router links)
|
||||
// Default: true
|
||||
enableLinkTracking: true,
|
||||
|
||||
// Require consent before sending tracking information to matomo
|
||||
// Default: false
|
||||
requireConsent: false,
|
||||
|
||||
// Whether to track the initial page view
|
||||
// Default: true
|
||||
trackInitialView: true,
|
||||
|
||||
// Run Matomo without cookies
|
||||
// Default: false
|
||||
disableCookies: false,
|
||||
|
||||
// Require consent before creating matomo session cookie
|
||||
// Default: false
|
||||
requireCookieConsent: false,
|
||||
|
||||
// Enable the heartbeat timer (https://developer.matomo.org/guides/tracking-javascript-guide#accurately-measure-the-time-spent-on-each-page)
|
||||
// Default: false
|
||||
enableHeartBeatTimer: false,
|
||||
|
||||
// Set the heartbeat timer interval
|
||||
// Default: 15
|
||||
heartBeatTimerInterval: 15,
|
||||
|
||||
// Whether or not to log debug information
|
||||
// Default: false
|
||||
debug: false,
|
||||
|
||||
// UserID passed to Matomo (see https://developer.matomo.org/guides/tracking-javascript-guide#user-id)
|
||||
// Default: undefined
|
||||
userId: undefined,
|
||||
|
||||
// Share the tracking cookie across subdomains (see https://developer.matomo.org/guides/tracking-javascript-guide#measuring-domains-andor-sub-domains)
|
||||
// Default: undefined, example '*.example.com'
|
||||
cookieDomain: undefined,
|
||||
|
||||
// Tell Matomo the website domain so that clicks on these domains are not tracked as 'Outlinks'
|
||||
// Default: undefined, example: '*.example.com'
|
||||
domains: "*.troplo.com",
|
||||
|
||||
// A list of pre-initialization actions that run before matomo is loaded
|
||||
// Default: []
|
||||
// Example: [
|
||||
// ['API_method_name', parameter_list],
|
||||
// ['setCustomVariable','1','VisitorType','Member'],
|
||||
// ['appendToTrackingUrl', 'new_visit=1'],
|
||||
// etc.
|
||||
// ]
|
||||
preInitActions: [],
|
||||
|
||||
// A function to determine whether to track an interaction as a site search
|
||||
// instead of as a page view. If not a function, all interactions will be
|
||||
// tracked as page views. Receives the new route as an argument, and
|
||||
// returns either an object of keyword, category (optional) and resultsCount
|
||||
// (optional) to track as a site search, or a falsey value to track as a page
|
||||
// view.
|
||||
// Default: false, i.e. track all interactions as page views
|
||||
// Example: (to) => {
|
||||
// if (to.query.q && to.name === 'search') {
|
||||
// return { keyword: to.query.q, category: to.params.category }
|
||||
// } else {
|
||||
// return null
|
||||
// }
|
||||
// }
|
||||
trackSiteSearch: false
|
||||
})
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
vuetify,
|
||||
store,
|
||||
render: (h) => h(App)
|
||||
}).$mount("#app")
|
21
src/main.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* main.ts
|
||||
*
|
||||
* Bootstraps Vuetify and other plugins then mounts the App`
|
||||
*/
|
||||
|
||||
// Plugins
|
||||
import { registerPlugins } from "@/plugins"
|
||||
|
||||
// Components
|
||||
import "./styles/index.css"
|
||||
import App from "./App.vue"
|
||||
|
||||
// Composables
|
||||
import { createApp } from "vue"
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
registerPlugins(app)
|
||||
|
||||
app.mount("#app")
|
5
src/pages/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Pages
|
||||
|
||||
Vue components created in this folder will automatically be converted to navigatable routes.
|
||||
|
||||
Full documentation for this feature can be found in the Official [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) repository.
|
|
@ -5,7 +5,9 @@
|
|||
<v-container class="text-center">
|
||||
<p class="text-h4">Not Found.</p>
|
||||
<p class="subtitle">This route does not exist.</p>
|
||||
<v-btn text @click="$router.push('/')">Go Home</v-btn>
|
||||
<v-btn variant="tonal" class="mt-2" @click="$router.push('/')"
|
||||
>Go Home</v-btn
|
||||
>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-container>
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-container class="justify-center text-center">
|
||||
<p class="text-h4">Contact</p>
|
||||
<p class="text-h4 mb-4">Contact</p>
|
||||
<v-row>
|
||||
<v-col
|
||||
md="3"
|
||||
|
@ -13,7 +13,18 @@
|
|||
>
|
||||
<v-card class="rounded-xl" elevation="8">
|
||||
<br />
|
||||
<v-icon>{{ contact.icon }}</v-icon>
|
||||
<v-icon
|
||||
v-if="contact.icon !== 'flowinity' && contact.icon !== 'discord'"
|
||||
>{{ contact.icon }}</v-icon
|
||||
>
|
||||
<FlowinityLogo
|
||||
style="width: 24px"
|
||||
v-else-if="contact.icon === 'flowinity'"
|
||||
/>
|
||||
<DiscordLogo
|
||||
style="width: 24px"
|
||||
v-else-if="contact.icon === 'discord'"
|
||||
/>
|
||||
<v-card-title class="justify-center">{{
|
||||
contact.title
|
||||
}}</v-card-title>
|
||||
|
@ -29,11 +40,21 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FlowinityLogo from "@/components/FlowinityLogo.vue"
|
||||
import DiscordLogo from "@/components/DiscordLogo.vue"
|
||||
|
||||
export default {
|
||||
name: "Contact",
|
||||
data() {
|
||||
return {
|
||||
contacts: [
|
||||
{
|
||||
id: 0,
|
||||
icon: "flowinity",
|
||||
title: "Flowinity",
|
||||
displayName: "Troplo",
|
||||
url: "https://flowinity.com/u/Troplo"
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
icon: "mdi-email",
|
||||
|
@ -50,9 +71,9 @@ export default {
|
|||
},*/
|
||||
{
|
||||
id: 3,
|
||||
icon: "mdi-discord",
|
||||
icon: "discord",
|
||||
title: "Discord",
|
||||
displayName: "Troplo#8495",
|
||||
displayName: "@troplo",
|
||||
url: "https://discord.com/users/692259321907773460"
|
||||
},
|
||||
{
|
||||
|
@ -89,16 +110,13 @@ export default {
|
|||
title: "Gitea",
|
||||
displayName: "Troplo",
|
||||
url: "https://git.troplo.com/Troplo"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
icon: "mdi-gitlab",
|
||||
title: "GitLab",
|
||||
displayName: "Troplo",
|
||||
url: "https://gitlab.com/Troplo"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DiscordLogo,
|
||||
FlowinityLogo
|
||||
}
|
||||
}
|
||||
</script>
|
597
src/pages/index.vue
Normal file
|
@ -0,0 +1,597 @@
|
|||
<template>
|
||||
<div id="projects">
|
||||
<v-carousel
|
||||
:style="`height: calc(100vh - ${announcementsStore.navbarOffset}px)`"
|
||||
hide-delimiters
|
||||
cycle
|
||||
:show-arrows="!display.mobile.value"
|
||||
:interval="7000"
|
||||
>
|
||||
<v-carousel-item
|
||||
v-for="(project, index) in carousel"
|
||||
:key="index"
|
||||
gradient="to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)"
|
||||
:src="project.src"
|
||||
cover
|
||||
class="position-relative"
|
||||
>
|
||||
<div
|
||||
class="d-flex flex-column align-center justify-center w-100 h-100 cursor-auto"
|
||||
>
|
||||
<v-card
|
||||
class="d-flex flex-column align-center justify-center rounded-xl pb-4 px-4"
|
||||
style="background: rgb(18, 18, 18, 0)"
|
||||
:elevation="0"
|
||||
>
|
||||
<v-card-title
|
||||
class="text-h3 d-flex justify-center"
|
||||
style="text-shadow: 0 0 10px black"
|
||||
>
|
||||
<component
|
||||
:is="project.logo"
|
||||
v-if="project.logo"
|
||||
style="width: 52px"
|
||||
class="mr-4"
|
||||
/>
|
||||
{{ project.name }}
|
||||
</v-card-title>
|
||||
|
||||
<p class="text-center" style="text-shadow: 0 0 10px black">
|
||||
{{ project.description }}
|
||||
</p>
|
||||
<v-btn
|
||||
v-for="link in project.links"
|
||||
:key="link.name"
|
||||
color="blue"
|
||||
class="mt-4"
|
||||
:variant="link.link || link.click ? 'elevated' : 'text'"
|
||||
:href="link.link"
|
||||
:ripple="false"
|
||||
target="_blank"
|
||||
@click="link.click && link.click()"
|
||||
>
|
||||
<v-icon class="mr-2" v-if="link.link"> mdi-open-in-new </v-icon>
|
||||
{{ link.name }}
|
||||
</v-btn>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-carousel-item>
|
||||
<v-btn
|
||||
icon
|
||||
class="position-absolute mb-6"
|
||||
style="bottom: 0; left: 50%; transform: translateX(-50%)"
|
||||
@click="scrollToProjects"
|
||||
>
|
||||
<v-icon>mdi-arrow-down</v-icon>
|
||||
</v-btn>
|
||||
</v-carousel>
|
||||
<v-container id="projects-list" :fluid="true">
|
||||
<p class="justify-center text-center text-h4 mb-4">My Projects</p>
|
||||
<v-row>
|
||||
<v-col
|
||||
v-for="(project, index) in getVisible"
|
||||
:key="index"
|
||||
cols="12"
|
||||
md="6"
|
||||
lg="3"
|
||||
xl="4"
|
||||
>
|
||||
<v-card
|
||||
class="rounded-xl troplo-p"
|
||||
elevation="8"
|
||||
:color="highlight === project.id ? '#303030' : ''"
|
||||
>
|
||||
<v-hover>
|
||||
<template v-slot:default="{ isHovering, props }">
|
||||
<v-img
|
||||
style="min-height: 100px"
|
||||
v-bind="props"
|
||||
:alt="'Image of ' + project.name"
|
||||
:src="getImage(project.internalName)"
|
||||
>
|
||||
<a :href="getImage(project.internalName)" target="_blank">
|
||||
<v-overlay
|
||||
:model-value="isHovering!"
|
||||
class="align-center justify-center"
|
||||
:contained="true"
|
||||
scrim="#000000"
|
||||
>
|
||||
<v-icon size="large" color="white"
|
||||
>mdi-open-in-new</v-icon
|
||||
>
|
||||
</v-overlay>
|
||||
</a>
|
||||
<template #placeholder>
|
||||
<v-row
|
||||
align="center"
|
||||
class="fill-height ma-0"
|
||||
justify="center"
|
||||
>
|
||||
<v-progress-circular
|
||||
color="grey lighten-5"
|
||||
indeterminate
|
||||
/>
|
||||
</v-row>
|
||||
</template>
|
||||
</v-img>
|
||||
</template>
|
||||
</v-hover>
|
||||
|
||||
<v-card-title class="text-wrap"
|
||||
>{{ project.name }}
|
||||
<v-chip
|
||||
v-if="project.release"
|
||||
variant="outlined"
|
||||
class="ml-2"
|
||||
size="small"
|
||||
>
|
||||
<v-icon size="18" class="mr-1 ml-n1">mdi-clock</v-icon>
|
||||
{{ project.release }}
|
||||
</v-chip></v-card-title
|
||||
>
|
||||
|
||||
<v-card-text class="text-wrap">
|
||||
<span style="white-space: pre-line; overflow-wrap: anywhere">{{
|
||||
project.description
|
||||
}}</span>
|
||||
</v-card-text>
|
||||
|
||||
<v-divider class="mx-4"></v-divider>
|
||||
|
||||
<v-card-title class="text-wrap">Information</v-card-title>
|
||||
|
||||
<v-card-text class="d-flex flex-wrap text-wrap" style="gap: 6px">
|
||||
<ChipTag v-for="tag in project.tags" :key="tag.name" :tag="tag" />
|
||||
</v-card-text>
|
||||
|
||||
<v-divider v-if="project.links.length" class="mx-4"></v-divider>
|
||||
|
||||
<v-card-actions v-if="project.links.length" class="mx-2">
|
||||
<v-btn
|
||||
v-for="link in project.links"
|
||||
:key="link.name"
|
||||
color="blue"
|
||||
variant="text"
|
||||
target="_blank"
|
||||
:href="link.link"
|
||||
>
|
||||
<v-icon v-if="link.icon" class="mr-1">{{ link.icon }}</v-icon>
|
||||
{{ link.name }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue"
|
||||
import { useDisplay } from "vuetify"
|
||||
import { useAnnouncementsStore } from "@/stores/announcements.store"
|
||||
import ChipTag from "@/components/ChipTag.vue"
|
||||
import FlowinityLogo from "@/components/FlowinityLogo.vue"
|
||||
|
||||
const display = useDisplay()
|
||||
|
||||
const highlight = ref<number | null>(null)
|
||||
|
||||
enum Tags {
|
||||
Active,
|
||||
Vue,
|
||||
Vuetify,
|
||||
Express,
|
||||
TypeScript,
|
||||
Android,
|
||||
Kotlin,
|
||||
GraphQL,
|
||||
Firebase,
|
||||
Inactive,
|
||||
DevelopmentHalted,
|
||||
Crystal,
|
||||
NoLongerAffiliated,
|
||||
New
|
||||
}
|
||||
|
||||
const announcementsStore = useAnnouncementsStore()
|
||||
|
||||
const tags = {
|
||||
[Tags.Active]: {
|
||||
name: "Active",
|
||||
icon: "mdi-check-circle",
|
||||
color: "success"
|
||||
},
|
||||
[Tags.Vue]: {
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
[Tags.Vuetify]: {
|
||||
name: "Vuetify",
|
||||
icon: "mdi-vuetify",
|
||||
link: "https://vuetifyjs.com",
|
||||
color: "#2196F3"
|
||||
},
|
||||
[Tags.Express]: {
|
||||
name: "Express",
|
||||
icon: "mdi-nodejs",
|
||||
link: "https://expressjs.com"
|
||||
},
|
||||
[Tags.TypeScript]: {
|
||||
name: "TypeScript",
|
||||
icon: "mdi-language-typescript",
|
||||
link: "https://typescriptlang.org",
|
||||
color: "#007acc"
|
||||
},
|
||||
[Tags.Android]: {
|
||||
name: "Android",
|
||||
icon: "mdi-android",
|
||||
link: "https://android.com",
|
||||
color: "#3ddc84",
|
||||
textColor: "black"
|
||||
},
|
||||
[Tags.Kotlin]: {
|
||||
name: "Kotlin",
|
||||
icon: "mdi-language-kotlin",
|
||||
link: "https://kotlinlang.org",
|
||||
color: "#E24462"
|
||||
},
|
||||
[Tags.GraphQL]: {
|
||||
name: "GraphQL",
|
||||
icon: "mdi-graphql",
|
||||
link: "https://graphql.org",
|
||||
color: "#E10098"
|
||||
},
|
||||
[Tags.Firebase]: {
|
||||
name: "Google APIs",
|
||||
icon: "mdi-firebase",
|
||||
link: "https://firebase.com",
|
||||
color: "#FFCA28"
|
||||
},
|
||||
[Tags.Inactive]: {
|
||||
name: "Discontinued",
|
||||
color: "error",
|
||||
icon: "mdi-alert-circle"
|
||||
},
|
||||
[Tags.DevelopmentHalted]: {
|
||||
name: "Halted",
|
||||
icon: "mdi-pause-octagon",
|
||||
color: "warning"
|
||||
},
|
||||
[Tags.Crystal]: {
|
||||
color: "white",
|
||||
name: "Crystal",
|
||||
icon: "crystal",
|
||||
link: "https://crystal-lang.org"
|
||||
},
|
||||
[Tags.NoLongerAffiliated]: {
|
||||
name: "No longer affiliated",
|
||||
icon: "mdi-help-circle",
|
||||
color: "yellow"
|
||||
},
|
||||
[Tags.New]: {
|
||||
name: "New",
|
||||
icon: "mdi-new-box",
|
||||
color: "success"
|
||||
}
|
||||
} as Record<
|
||||
Tags,
|
||||
{
|
||||
name: string
|
||||
link?: string
|
||||
color?: string
|
||||
icon?: string
|
||||
}
|
||||
>
|
||||
|
||||
const carousel = [
|
||||
{
|
||||
src: "/images/flowinity-update-comms.png",
|
||||
name: "Flowinity",
|
||||
logo: FlowinityLogo,
|
||||
description: "The versatile online social and collaborative platform.",
|
||||
links: [
|
||||
{
|
||||
name: "Get Started",
|
||||
link: "https://flowinity.com/home?mtm_campaign=troplo-carousel"
|
||||
}
|
||||
],
|
||||
tags: []
|
||||
},
|
||||
{
|
||||
src: "/images/flowforms.png",
|
||||
name: "FlowForms",
|
||||
description: "The free flow chart form builder.",
|
||||
tags: [tags[Tags.New]],
|
||||
links: [
|
||||
{
|
||||
name: "Learn More",
|
||||
click: () => {
|
||||
scrollToProjects()
|
||||
setTimeout(() => {
|
||||
highlight.value = 13
|
||||
}, 500)
|
||||
setTimeout(() => {
|
||||
highlight.value = null
|
||||
}, 1500)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
] as {
|
||||
src: string
|
||||
name: string
|
||||
description: string
|
||||
logo?: any
|
||||
tags: {
|
||||
name: string
|
||||
link?: string
|
||||
color?: string
|
||||
icon?: string
|
||||
}[]
|
||||
links: {
|
||||
name: string
|
||||
link?: string
|
||||
click?: () => void
|
||||
}[]
|
||||
}[]
|
||||
|
||||
const projects = [
|
||||
{
|
||||
id: 2,
|
||||
name: "Flowinity",
|
||||
release: "2021",
|
||||
internalName: "proj01",
|
||||
tags: [
|
||||
tags[Tags.Active],
|
||||
tags[Tags.Vue],
|
||||
tags[Tags.Vuetify],
|
||||
tags[Tags.Express],
|
||||
tags[Tags.TypeScript]
|
||||
],
|
||||
description: "The versatile online social and collaborative platform.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "Website",
|
||||
link: "https://flowinity.com/home?mtm_campaign=troplo-projects",
|
||||
icon: "mdi-web"
|
||||
},
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/Flowinity/Flowinity",
|
||||
icon: "mdi-github"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "FlowForms",
|
||||
release: "2024",
|
||||
internalName: "flowforms",
|
||||
tags: [
|
||||
tags[Tags.New],
|
||||
tags[Tags.Vue],
|
||||
tags[Tags.Vuetify],
|
||||
tags[Tags.GraphQL],
|
||||
tags[Tags.TypeScript]
|
||||
],
|
||||
description: "The free flow chart form builder.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "Early Access",
|
||||
link: "https://flowforms.troplo.com",
|
||||
icon: "mdi-web"
|
||||
},
|
||||
{
|
||||
name: "Provide Feedback",
|
||||
link: "https://flowforms.troplo.com/form/9ab39eb2-6fa0-41b1-b669-d665281b29b1",
|
||||
icon: "mdi-message-alert"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "Flowinity for Android",
|
||||
release: "2023",
|
||||
internalName: "flowinity-android",
|
||||
tags: [tags[Tags.Active], tags[Tags.Android], tags[Tags.Kotlin]],
|
||||
description:
|
||||
"The versatile online social and collaborative platform for Android.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "Google Play",
|
||||
link: "https://play.google.com/store/apps/details?id=com.troplo.privateuploader",
|
||||
icon: "mdi-google-play"
|
||||
},
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/Flowinity/Android",
|
||||
icon: "mdi-github"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Colubrina",
|
||||
internalName: "colubrina",
|
||||
tags: [
|
||||
tags[Tags.Inactive],
|
||||
tags[Tags.Vue],
|
||||
tags[Tags.Vuetify],
|
||||
tags[Tags.Express]
|
||||
],
|
||||
description:
|
||||
"NOTE: Colubrina is now part of Flowinity as Flowinity Communications.\n\nColubrina - a simple open source chat platform written in Vue, Vuetify, NodeJS, and Socket.io.",
|
||||
visible: true,
|
||||
release: "2022",
|
||||
links: [
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/Troplo/Colubrina",
|
||||
icon: "mdi-github"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "BetterCompass",
|
||||
internalName: "compass-vue",
|
||||
release: "2022",
|
||||
tags: [
|
||||
tags[Tags.Inactive],
|
||||
tags[Tags.Vue],
|
||||
tags[Tags.Vuetify],
|
||||
tags[Tags.Express]
|
||||
],
|
||||
description:
|
||||
"An open source modern frontend for the JDLF Compass School Manager written in Vue.js.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "Website",
|
||||
link: "https://compass.troplo.com",
|
||||
icon: "mdi-web"
|
||||
},
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/Troplo/BetterCompass",
|
||||
icon: "mdi-github"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "GeoGuess",
|
||||
internalName: "geoguess",
|
||||
release: "2022",
|
||||
description:
|
||||
"Contributed changes & fixes to the open source geography game GeoGuess.",
|
||||
visible: true,
|
||||
tags: [
|
||||
tags[Tags.Active],
|
||||
tags[Tags.Vue],
|
||||
tags[Tags.Vuetify],
|
||||
tags[Tags.Firebase]
|
||||
],
|
||||
links: [
|
||||
{
|
||||
name: "My Instance",
|
||||
link: "https://geo.troplo.com",
|
||||
icon: "mdi-web"
|
||||
},
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/GeoGuess/GeoGuess",
|
||||
icon: "mdi-github"
|
||||
},
|
||||
{
|
||||
name: "Website",
|
||||
link: "https://geoguess.games",
|
||||
icon: "mdi-web"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Mira",
|
||||
release: "2021",
|
||||
internalName: "mira",
|
||||
tags: [tags[Tags.Inactive], tags[Tags.Vue], tags[Tags.Crystal]],
|
||||
description: "EPUB reader written in Crystal and Vue.js.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/pinnoto/mira",
|
||||
icon: "mdi-github"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "Vixlatio",
|
||||
internalName: "vixlatio",
|
||||
release: "2022",
|
||||
tags: [tags[Tags.NoLongerAffiliated], tags[Tags.Vue], tags[Tags.Express]],
|
||||
description:
|
||||
"Developer for Vixlatio, a blazing fast gaming platform that is powered by the creativity of its users.",
|
||||
visible: true,
|
||||
links: []
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Kansas",
|
||||
internalName: "kansas",
|
||||
release: "2022",
|
||||
tags: [
|
||||
tags[Tags.DevelopmentHalted],
|
||||
tags[Tags.Vue],
|
||||
tags[Tags.Vuetify],
|
||||
tags[Tags.Express]
|
||||
],
|
||||
description: "Open source project tracking software written in Vue.js.",
|
||||
visible: true,
|
||||
links: []
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Troplo's Website",
|
||||
release: "2021",
|
||||
internalName: "troplo-website",
|
||||
tags: [tags[Tags.Active], tags[Tags.Vue], tags[Tags.Vuetify]],
|
||||
description: "The website you are viewing right now.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/Troplo/website",
|
||||
icon: "mdi-github"
|
||||
},
|
||||
{
|
||||
name: "Website",
|
||||
link: "https://troplo.com",
|
||||
icon: "mdi-web"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: "Kaverti",
|
||||
release: "2020",
|
||||
internalName: "kaverti",
|
||||
tags: [tags[Tags.Inactive], tags[Tags.Vue], tags[Tags.Express]],
|
||||
description:
|
||||
"A 3D sandbox game, and social avatar platform written in Express and Vue.js.",
|
||||
visible: true,
|
||||
links: []
|
||||
}
|
||||
]
|
||||
|
||||
const getVisible = computed(() => projects.filter((i) => i.visible))
|
||||
|
||||
const getImage = (image: string) => {
|
||||
return `/images/${image}.png`
|
||||
}
|
||||
|
||||
const scrollToProjects = () => {
|
||||
const element = document.getElementById("projects-list")!
|
||||
const y =
|
||||
element.getBoundingClientRect().top +
|
||||
window.scrollY -
|
||||
announcementsStore.navbarOffset
|
||||
window.scrollTo({ top: y + 6, behavior: "smooth" })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.v-chip--disabled {
|
||||
opacity: 1 !important;
|
||||
pointer-events: inherit !important;
|
||||
user-select: inherit !important;
|
||||
}
|
||||
</style>
|
107
src/pages/news/[id].vue
Normal file
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<v-img
|
||||
v-if="announcement"
|
||||
:src="announcement.image!"
|
||||
:alt="announcement.title"
|
||||
height="300"
|
||||
class="d-flex mx-auto"
|
||||
aspect-ratio="32/5"
|
||||
cover
|
||||
/>
|
||||
<v-container max-width="1400">
|
||||
<div v-if="!loading && announcement">
|
||||
<div>
|
||||
<v-card-title class="text-h4 text-wrap text-center">
|
||||
{{ announcement.title }}
|
||||
</v-card-title>
|
||||
<div class="d-flex justify-center">
|
||||
<div class="d-flex flex-column align-center">
|
||||
<v-avatar size="40">
|
||||
<v-img
|
||||
v-if="announcement.flowinityUser?.avatar"
|
||||
:src="announcement.flowinityUser.avatar"
|
||||
:alt="announcement.flowinityUser.username"
|
||||
></v-img>
|
||||
</v-avatar>
|
||||
<p class="v-card-subtitle">
|
||||
{{ announcement.flowinityUser?.username }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="v-card-subtitle">
|
||||
{{ dayjs(announcement.createdAt).format("Do of MMMM YYYY") }}
|
||||
<small
|
||||
v-if="
|
||||
announcement.bannerExpiry &&
|
||||
dayjs(announcement.bannerExpiry).isAfter(dayjs())
|
||||
"
|
||||
>
|
||||
(Expires {{ dayjs(announcement.bannerExpiry).fromNow() }})
|
||||
</small>
|
||||
<small v-else-if="announcement.bannerExpiry" class="text-red">
|
||||
(Expired {{ dayjs(announcement.bannerExpiry).fromNow() }})
|
||||
</small>
|
||||
</p>
|
||||
<v-divider class="my-2" />
|
||||
<p
|
||||
class="ml-4"
|
||||
v-if="announcement.content"
|
||||
v-html="mdAnnouncements.render(announcement.content)"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<v-skeleton-loader
|
||||
v-else-if="loading"
|
||||
:loading="true"
|
||||
class="mx-auto"
|
||||
height="100%"
|
||||
type="article"
|
||||
/>
|
||||
<div v-else class="d-flex justify-center flex-column">
|
||||
<v-card-title class="text-h4 text-wrap text-center">
|
||||
Announcement does not exist.
|
||||
</v-card-title>
|
||||
<v-btn to="/news"> Back to News </v-btn>
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from "vue"
|
||||
import { useRoute } from "vue-router"
|
||||
import { useApolloClient } from "@vue/apollo-composable"
|
||||
import { AnnouncementDocument, AnnouncementQuery } from "@/gql/graphql"
|
||||
import dayjs from "../../lib/dayjs"
|
||||
import mdAnnouncements from "@/lib/mdAnnouncements"
|
||||
|
||||
const route = useRoute()
|
||||
const id = computed(() => <string>route.params.id)
|
||||
const apollo = useApolloClient()
|
||||
const announcement = ref<AnnouncementQuery["announcement"] | null>(null)
|
||||
const loading = ref(false)
|
||||
|
||||
async function getAnnouncement() {
|
||||
loading.value = true
|
||||
const { data } = await apollo.client.query({
|
||||
query: AnnouncementDocument,
|
||||
variables: { announcementId: id.value }
|
||||
})
|
||||
announcement.value = data.announcement
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getAnnouncement()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => id.value,
|
||||
() => {
|
||||
getAnnouncement()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
69
src/pages/news/index.vue
Normal file
|
@ -0,0 +1,69 @@
|
|||
<template>
|
||||
<v-container max-width="1400">
|
||||
<div v-if="!loading" class="d-flex flex-column" style="gap: 12px">
|
||||
<v-card
|
||||
v-for="announcement in news"
|
||||
:key="announcement.id"
|
||||
class="rounded-xl"
|
||||
>
|
||||
<div :class="{ 'd-flex': !display.mobile.value }">
|
||||
<v-img
|
||||
:src="announcement.image"
|
||||
aspect-ratio="16/9"
|
||||
max-width="400"
|
||||
min-width="400"
|
||||
cover
|
||||
/>
|
||||
<div class="d-flex flex-column justify-center flex-0">
|
||||
<v-card-title class="text-h4 text-wrap">{{
|
||||
announcement.title
|
||||
}}</v-card-title>
|
||||
<v-card-subtitle>
|
||||
{{ dayjs(announcement.createdAt).format("Do of MMMM YYYY") }}
|
||||
</v-card-subtitle>
|
||||
<v-card-text>
|
||||
{{ announcement.description }}
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn
|
||||
:to="`/news/${announcement.id}`"
|
||||
variant="outlined"
|
||||
class="ml-2"
|
||||
>Read more</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</div>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { AnnouncementsQuery } from "@/gql/graphql"
|
||||
import { onMounted, ref } from "vue"
|
||||
import { useAnnouncementsStore } from "@/stores/announcements.store"
|
||||
import dayjs from "../../lib/dayjs"
|
||||
import { useDisplay } from "vuetify"
|
||||
|
||||
const news = ref<AnnouncementsQuery["announcements"]>([])
|
||||
const announcementsStore = useAnnouncementsStore()
|
||||
const loading = ref(false)
|
||||
const page = ref(1)
|
||||
const display = useDisplay()
|
||||
|
||||
async function getNews() {
|
||||
loading.value = true
|
||||
const data = await announcementsStore.getAnnouncements({
|
||||
page: page.value
|
||||
})
|
||||
news.value = data
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getNews()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
3
src/plugins/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Plugins
|
||||
|
||||
Plugins are a way to extend the functionality of your Vue application. Use this folder for registering plugins that you want to use globally.
|
17
src/plugins/apollo.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client"
|
||||
|
||||
const httpLink = createHttpLink({
|
||||
// You should use an absolute URL here
|
||||
uri: import.meta.env.DEV ? "/graphql" : "https://api.troplo.com/graphql"
|
||||
})
|
||||
|
||||
// Cache implementation
|
||||
const cache = new InMemoryCache()
|
||||
|
||||
// Create the apollo client
|
||||
const apolloClient = new ApolloClient({
|
||||
link: httpLink,
|
||||
cache
|
||||
})
|
||||
|
||||
export default apolloClient
|
20
src/plugins/index.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* plugins/index.ts
|
||||
*
|
||||
* Automatically included in `./src/main.ts`
|
||||
*/
|
||||
|
||||
// Plugins
|
||||
import vuetify from "./vuetify"
|
||||
import router from "../router"
|
||||
|
||||
// Types
|
||||
import type { App } from "vue"
|
||||
import { pinia } from "@/stores"
|
||||
import { DefaultApolloClient } from "@vue/apollo-composable"
|
||||
import apolloClient from "@/plugins/apollo"
|
||||
|
||||
export function registerPlugins(app: App) {
|
||||
app.provide(DefaultApolloClient, apolloClient)
|
||||
app.use(vuetify).use(router).use(pinia)
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
import Vue from "vue"
|
||||
import Vuetify from "vuetify/lib/framework"
|
||||
import colors from "vuetify/es5/util/colors"
|
||||
|
||||
Vue.use(Vuetify)
|
||||
|
||||
export default new Vuetify({
|
||||
theme: {
|
||||
themes: {
|
||||
light: {
|
||||
primary: colors.blue.lighten3,
|
||||
secondary: colors.grey.darken1,
|
||||
accent: colors.shades.black,
|
||||
error: colors.red.accent3,
|
||||
dark: "#151515",
|
||||
text: "#000000"
|
||||
},
|
||||
dark: {
|
||||
primary: colors.blue,
|
||||
dark: "#151515",
|
||||
text: "#ffffff"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
26
src/plugins/vuetify.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* plugins/vuetify.ts
|
||||
*
|
||||
* Framework documentation: https://vuetifyjs.com`
|
||||
*/
|
||||
|
||||
// Styles
|
||||
import "@mdi/font/css/materialdesignicons.css"
|
||||
import "vuetify/styles"
|
||||
|
||||
// Composables
|
||||
import { createVuetify } from "vuetify"
|
||||
|
||||
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
|
||||
export default createVuetify({
|
||||
theme: {
|
||||
defaultTheme: "dark",
|
||||
themes: {
|
||||
dark: {
|
||||
colors: {
|
||||
surface: "#181818"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
|
@ -1,54 +0,0 @@
|
|||
import Vue from "vue"
|
||||
import VueRouter from "vue-router"
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const routes = [
|
||||
/*{
|
||||
path: "/",
|
||||
name: "Home",
|
||||
component: () => import(/* webpackChunkName: "home" *//* "../views/Home.vue")
|
||||
},*/
|
||||
{
|
||||
path: "/",
|
||||
name: "Home",
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (about.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "projects" */ "../views/Projects.vue")
|
||||
},
|
||||
{
|
||||
path: "/projects",
|
||||
name: "Projects",
|
||||
redirect: "/",
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (about.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
},
|
||||
{
|
||||
path: "/contact",
|
||||
name: "Contact",
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (about.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "contact" */ "../views/Contact.vue")
|
||||
},
|
||||
{
|
||||
path: "*",
|
||||
name: "Not Found",
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (about.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "notFound" */ "../views/NotFound.vue")
|
||||
}
|
||||
]
|
||||
|
||||
const router = new VueRouter({
|
||||
routes,
|
||||
mode: "history"
|
||||
})
|
||||
|
||||
export default router
|
35
src/router/index.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* router/index.ts
|
||||
*
|
||||
* Automatic routes for `./src/pages/*.vue`
|
||||
*/
|
||||
|
||||
// Composables
|
||||
import { createRouter, createWebHistory } from 'vue-router/auto'
|
||||
import { routes } from 'vue-router/auto-routes'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes,
|
||||
})
|
||||
|
||||
// Workaround for https://github.com/vitejs/vite/issues/11804
|
||||
router.onError((err, to) => {
|
||||
if (err?.message?.includes?.('Failed to fetch dynamically imported module')) {
|
||||
if (!localStorage.getItem('vuetify:dynamic-reload')) {
|
||||
console.log('Reloading page to fix dynamic import error')
|
||||
localStorage.setItem('vuetify:dynamic-reload', 'true')
|
||||
location.assign(to.fullPath)
|
||||
} else {
|
||||
console.error('Dynamic import error, reloading page did not fix it', err)
|
||||
}
|
||||
} else {
|
||||
console.error(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.isReady().then(() => {
|
||||
localStorage.removeItem('vuetify:dynamic-reload')
|
||||
})
|
||||
|
||||
export default router
|
|
@ -1,21 +0,0 @@
|
|||
import Vue from "vue"
|
||||
import Vuex from "vuex"
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
site: {
|
||||
name: "Troplo's Website",
|
||||
route: "Home"
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
setSite(state, data) {
|
||||
state.site = data
|
||||
},
|
||||
setRoute(state, data) {
|
||||
state.site.route = data
|
||||
}
|
||||
}
|
||||
})
|
47
src/stores/announcements.store.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { defineStore } from "pinia"
|
||||
import {
|
||||
AnnouncementsDocument,
|
||||
AnnouncementsInput,
|
||||
AnnouncementsQuery
|
||||
} from "@/gql/graphql"
|
||||
import { nextTick, ref, watch } from "vue"
|
||||
import { useApolloClient } from "@vue/apollo-composable"
|
||||
import { useDisplay } from "vuetify"
|
||||
|
||||
export const useAnnouncementsStore = defineStore("announcements", () => {
|
||||
const banners = ref<AnnouncementsQuery["announcements"]>([])
|
||||
|
||||
async function getAnnouncements(input: AnnouncementsInput = {}) {
|
||||
const apolloClient = useApolloClient()
|
||||
const { data } = await apolloClient.client.query({
|
||||
query: AnnouncementsDocument,
|
||||
variables: { input }
|
||||
})
|
||||
return data.announcements
|
||||
}
|
||||
|
||||
const navbarOffset = ref(0)
|
||||
const display = useDisplay()
|
||||
|
||||
watch(
|
||||
() => [banners.value, display.width.value, display.height.value],
|
||||
async () => {
|
||||
await nextTick(() => {
|
||||
let offset = 64
|
||||
for (const banner of banners.value) {
|
||||
const element = document.getElementById(`banner-${banner.id}`)
|
||||
if (element) {
|
||||
offset += element.clientHeight
|
||||
}
|
||||
}
|
||||
navbarOffset.value = offset
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
banners,
|
||||
getAnnouncements,
|
||||
navbarOffset
|
||||
}
|
||||
})
|
3
src/stores/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { createPinia } from "pinia"
|
||||
|
||||
export const pinia = createPinia()
|
3
src/styles/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Styles
|
||||
|
||||
This directory is for configuring the styles of the application.
|
|
@ -19,10 +19,17 @@
|
|||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.theme--dark.v-sheet {
|
||||
background-color: #151515 ;
|
||||
border-color: #151515;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#header .theme--dark.v-sheet {
|
||||
background-color: #151515 !important;
|
||||
border-color: #151515 !important;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.card {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -50,4 +57,22 @@
|
|||
background-color: #151515;
|
||||
border-radius: 20px;
|
||||
border: 3px solid #151515;
|
||||
}
|
||||
|
||||
.rounded-xl {
|
||||
border-radius: 12px !important;
|
||||
}
|
||||
|
||||
.v-btn--variant-outlined,
|
||||
.v-chip--variant-outlined,
|
||||
.v-card--variant-outlined,
|
||||
.v-avatar--variant-outlined,
|
||||
.v-list-item--variant-outlined
|
||||
{
|
||||
border-color: hsla(0, 0%, 100%, 0.12) !important;
|
||||
}
|
||||
|
||||
.v-btn {
|
||||
letter-spacing: inherit !important;
|
||||
text-transform: inherit !important;
|
||||
}
|
12
src/styles/settings.scss
Normal file
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* src/styles/settings.scss
|
||||
*
|
||||
* Configures SASS variables and Vuetify overwrites
|
||||
*/
|
||||
|
||||
// https://vuetifyjs.com/features/sass-variables/`
|
||||
// @use 'vuetify/settings' with (
|
||||
// $color-pack: false
|
||||
// );
|
||||
|
||||
// change the rounded-xl class to 12px
|
|
@ -1,26 +0,0 @@
|
|||
<template>
|
||||
<div id="home">
|
||||
<v-container class="text-center">
|
||||
<v-card elevation="8" class="troplo-header rounded-xl">
|
||||
<v-container>
|
||||
<div v-if="!$vuetify.breakpoint.mobile" class="troplo-header-title">
|
||||
Troplo
|
||||
</div>
|
||||
<div
|
||||
v-if="$vuetify.breakpoint.mobile"
|
||||
class="troplo-header-title"
|
||||
style="background: -webkit-radial-gradient(#0179f3, #0190ea)"
|
||||
>
|
||||
T
|
||||
</div>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Home"
|
||||
}
|
||||
</script>
|
|
@ -1,11 +0,0 @@
|
|||
<template>
|
||||
<div id="nexus"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Nexus"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,566 +0,0 @@
|
|||
<template>
|
||||
<div id="projects">
|
||||
<v-container :fluid="$vuetify.breakpoint.lgAndDown">
|
||||
<v-card elevation="8" class="troplo-header rounded-xl text-center" v-if="false">
|
||||
<v-container>
|
||||
<div v-if="!$vuetify.breakpoint.mobile" class="troplo-header-title">
|
||||
Troplo
|
||||
</div>
|
||||
<div
|
||||
v-if="$vuetify.breakpoint.mobile"
|
||||
class="troplo-header-title"
|
||||
style="background: -webkit-radial-gradient(#0179f3, #0190ea)"
|
||||
>
|
||||
T
|
||||
</div>
|
||||
</v-container>
|
||||
</v-card>
|
||||
<p class="justify-center text-center text-h4">My Projects</p>
|
||||
<v-row>
|
||||
<v-col lg="3" v-for="(project, index) in getVisible" :key="index">
|
||||
<v-card class="rounded-xl troplo-p" elevation="8">
|
||||
<v-hover v-slot="{ hover }">
|
||||
<v-img :alt="'Image of ' + project.name" :src="getImage(project.internalName)">
|
||||
<a :href="getImage(project.internalName)" target="_blank">
|
||||
<v-fade-transition v-if="hover">
|
||||
<v-overlay absolute>
|
||||
<v-icon large>mdi-open-in-new</v-icon>
|
||||
</v-overlay>
|
||||
</v-fade-transition>
|
||||
</a>
|
||||
</v-img>
|
||||
</v-hover>
|
||||
|
||||
<v-card-title>{{ project.name }}</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<span style="white-space: pre-line; overflow-wrap: anywhere">{{
|
||||
project.description
|
||||
}}</span>
|
||||
</v-card-text>
|
||||
|
||||
<v-divider class="mx-4"></v-divider>
|
||||
|
||||
<v-card-title>Information</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-chip-group column>
|
||||
<v-chip
|
||||
v-for="tag in project.tags"
|
||||
:key="tag.id"
|
||||
:href="tag.link"
|
||||
:color="tag.color"
|
||||
:disabled="!tag.link"
|
||||
class="troplo-p"
|
||||
:text-color="tag.textColor || 'white'"
|
||||
style="opacity: 1"
|
||||
>
|
||||
<v-img
|
||||
v-if="tag.icon === 'crystal'"
|
||||
src="../assets/icons/crystal-icon.svg"
|
||||
width="30"
|
||||
height="30"
|
||||
></v-img>
|
||||
<v-icon v-if="tag.icon !== 'crystal'">{{ tag.icon }}</v-icon>
|
||||
<template v-if="tag.icon"> </template>
|
||||
{{ tag.name }}
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
</v-card-text>
|
||||
|
||||
<v-divider v-if="project.links.length" class="mx-4"></v-divider>
|
||||
|
||||
<v-card-actions v-if="project.links.length">
|
||||
<v-btn
|
||||
v-for="link in project.links"
|
||||
:key="link.name"
|
||||
color="blue"
|
||||
text
|
||||
:href="link.link"
|
||||
>
|
||||
{{ link.name }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Projects",
|
||||
data() {
|
||||
return {
|
||||
projects: [
|
||||
/* {
|
||||
id: 1,
|
||||
name: "Kaverti",
|
||||
internalName: "kaverti",
|
||||
tags: [
|
||||
{
|
||||
internalName: "inactive",
|
||||
name: "Discontinued",
|
||||
color: "error",
|
||||
icon: "mdi-alert-circle"
|
||||
},
|
||||
{
|
||||
internalName: "vue",
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
{
|
||||
internalName: "express",
|
||||
name: "Express",
|
||||
icon: "mdi-nodejs",
|
||||
link: "https://expressjs.com"
|
||||
}
|
||||
],
|
||||
description:
|
||||
"A 3D sandbox game, and social avatar platform written in Express and Vue.js.",
|
||||
visible: true,
|
||||
links: []
|
||||
},*/
|
||||
{
|
||||
id: 2,
|
||||
name: "TroploPrivateUploader (TPU)",
|
||||
internalName: "proj01",
|
||||
tags: [
|
||||
{
|
||||
internalName: "active",
|
||||
name: "Active",
|
||||
icon: "mdi-check-circle",
|
||||
color: "success"
|
||||
},
|
||||
{
|
||||
internalName: "vue",
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
{
|
||||
internalName: "vuetify",
|
||||
name: "Vuetify",
|
||||
icon: "mdi-vuetify",
|
||||
link: "https://vuetifyjs.com",
|
||||
color: "#2196F3"
|
||||
},
|
||||
{
|
||||
internalName: "express",
|
||||
name: "Express",
|
||||
icon: "mdi-nodejs",
|
||||
link: "https://expressjs.com"
|
||||
},
|
||||
{
|
||||
internalName: "typescript",
|
||||
name: "TypeScript",
|
||||
icon: "mdi-language-typescript",
|
||||
link: "https://typescriptlang.org",
|
||||
color: "#007acc"
|
||||
}
|
||||
],
|
||||
description:
|
||||
"The versatile image hosting service, now for everyone with a unique feature-set.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "Website",
|
||||
link: "https://privateuploader.com"
|
||||
},
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/PrivateUploader/PrivateUploader"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Colubrina",
|
||||
internalName: "colubrina",
|
||||
tags: [
|
||||
{
|
||||
internalName: "inactive",
|
||||
name: "Discontinued",
|
||||
color: "error",
|
||||
icon: "mdi-alert-circle"
|
||||
},
|
||||
{
|
||||
internalName: "vue",
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
{
|
||||
internalName: "vuetify",
|
||||
name: "Vuetify",
|
||||
icon: "mdi-vuetify",
|
||||
link: "https://vuetifyjs.com",
|
||||
color: "#2196F3"
|
||||
},
|
||||
{
|
||||
internalName: "express",
|
||||
name: "Express",
|
||||
icon: "mdi-nodejs",
|
||||
link: "https://expressjs.com"
|
||||
},
|
||||
],
|
||||
description:
|
||||
"NOTE: Colubrina is now part of TroploPrivateUploader as TPU Communications.\n\nColubrina - a simple open source chat platform written in Vue, Vuetify, NodeJS, and Socket.io.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "Website",
|
||||
link: "https://colubrina.troplo.com"
|
||||
},
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/Troplo/Colubrina"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "BetterCompass",
|
||||
internalName: "compass-vue",
|
||||
tags: [
|
||||
{
|
||||
internalName: "inactive",
|
||||
name: "Discontinued",
|
||||
color: "error",
|
||||
icon: "mdi-alert-circle"
|
||||
},
|
||||
{
|
||||
internalName: "vue",
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
{
|
||||
internalName: "vuetify",
|
||||
name: "Vuetify",
|
||||
icon: "mdi-vuetify",
|
||||
link: "https://vuetifyjs.com",
|
||||
color: "#2196F3"
|
||||
},
|
||||
{
|
||||
internalName: "express",
|
||||
name: "Express",
|
||||
icon: "mdi-nodejs",
|
||||
link: "https://expressjs.com"
|
||||
}
|
||||
],
|
||||
description:
|
||||
"An open source modern frontend for the JDLF Compass School Manager written in Vue.js.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "Website",
|
||||
link: "https://compass.troplo.com"
|
||||
},
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/Troplo/BetterCompass"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "GeoGuess",
|
||||
internalName: "geoguess",
|
||||
description: "Contributed changes & fixes to the open source geography game GeoGuess.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "My Instance",
|
||||
link: "https://geo.troplo.com"
|
||||
},
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/GeoGuess/GeoGuess"
|
||||
},
|
||||
{
|
||||
name: "Website",
|
||||
link: "https://geoguess.games"
|
||||
}
|
||||
],
|
||||
tags: [
|
||||
{
|
||||
internalName: "active",
|
||||
name: "Active",
|
||||
icon: "mdi-check-circle",
|
||||
color: "success"
|
||||
},
|
||||
{
|
||||
internalName: "vue",
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
{
|
||||
internalName: "vuetify",
|
||||
name: "Vuetify",
|
||||
icon: "mdi-vuetify",
|
||||
link: "https://vuetifyjs.com",
|
||||
color: "#2196F3"
|
||||
},
|
||||
{
|
||||
internalName: "firebase",
|
||||
name: "Google APIs",
|
||||
icon: "mdi-firebase",
|
||||
link: "https://firebase.com"
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Mira",
|
||||
internalName: "mira",
|
||||
tags: [
|
||||
{
|
||||
internalName: "active",
|
||||
name: "Active",
|
||||
icon: "mdi-check-circle",
|
||||
color: "success"
|
||||
},
|
||||
{
|
||||
internalName: "vue",
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
{
|
||||
internalName: "crystal",
|
||||
color: "white",
|
||||
textColor: "black",
|
||||
name: "Crystal",
|
||||
icon: "crystal",
|
||||
link: "https://crystal-lang.org"
|
||||
}
|
||||
],
|
||||
description: "EPUB reader written in Crystal and Vue.js.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/pinnoto/mira"
|
||||
}
|
||||
]
|
||||
},
|
||||
/*{
|
||||
id: 10,
|
||||
name: "Vixlatio",
|
||||
internalName: "vixlatio",
|
||||
tags: [
|
||||
{
|
||||
internalName: "active",
|
||||
name: "Active",
|
||||
icon: "mdi-check-circle",
|
||||
color: "success"
|
||||
},
|
||||
{
|
||||
internalName: "vue",
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
{
|
||||
internalName: "express",
|
||||
name: "Express",
|
||||
icon: "mdi-nodejs",
|
||||
link: "https://expressjs.com"
|
||||
}
|
||||
],
|
||||
description:
|
||||
"Developer for Vixlatio, a blazing fast gaming platform that is powered by the creativity of its users.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "Website",
|
||||
link: "https://vixlatio.com"
|
||||
}
|
||||
]
|
||||
},*/
|
||||
{
|
||||
id: 5,
|
||||
name: "Berri",
|
||||
internalName: "berri",
|
||||
tags: [
|
||||
{
|
||||
internalName: "development-halted",
|
||||
name: "Halted",
|
||||
icon: "mdi-pause-octagon",
|
||||
color: "warning"
|
||||
},
|
||||
{
|
||||
internalName: "vue",
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
{
|
||||
internalName: "crystal",
|
||||
color: "white",
|
||||
name: "Crystal",
|
||||
textColor: "black",
|
||||
icon: "crystal",
|
||||
link: "https://crystal-lang.org"
|
||||
}
|
||||
],
|
||||
description:
|
||||
"Open source file hosting service which aims to be performant, and modular.\nName not finalized.",
|
||||
visible: true,
|
||||
links: {}
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Kansas",
|
||||
internalName: "kansas",
|
||||
tags: [
|
||||
{
|
||||
internalName: "development-halted",
|
||||
name: "Halted",
|
||||
icon: "mdi-pause-octagon",
|
||||
color: "warning"
|
||||
},
|
||||
{
|
||||
internalName: "vue",
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
{
|
||||
internalName: "vuetify",
|
||||
name: "Vuetify",
|
||||
icon: "mdi-vuetify",
|
||||
link: "https://vuetifyjs.com",
|
||||
color: "#2196F3"
|
||||
},
|
||||
{
|
||||
internalName: "express",
|
||||
name: "Express",
|
||||
icon: "mdi-nodejs",
|
||||
link: "https://expressjs.com"
|
||||
}
|
||||
],
|
||||
description:
|
||||
"Open source project tracking software written in Vue.js.",
|
||||
visible: true,
|
||||
links: {}
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Flowinity",
|
||||
internalName: "flowinity",
|
||||
tags: [
|
||||
{
|
||||
internalName: "development-halted",
|
||||
name: "Halted",
|
||||
icon: "mdi-pause-octagon",
|
||||
color: "warning"
|
||||
}
|
||||
],
|
||||
description: "A tech blog.",
|
||||
visible: true,
|
||||
links: {}
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Troplo's Website",
|
||||
internalName: "troplo-website",
|
||||
tags: [
|
||||
{
|
||||
internalName: "active",
|
||||
name: "Active",
|
||||
icon: "mdi-check-circle",
|
||||
color: "success"
|
||||
},
|
||||
{
|
||||
internalName: "vue",
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
{
|
||||
internalName: "vuetify",
|
||||
name: "Vuetify",
|
||||
icon: "mdi-vuetify",
|
||||
link: "https://vuetifyjs.com",
|
||||
color: "#2196F3"
|
||||
},
|
||||
],
|
||||
description: "The website you are viewing right now.",
|
||||
visible: true,
|
||||
links: [
|
||||
{
|
||||
name: "GitHub",
|
||||
link: "https://github.com/Troplo/website"
|
||||
},
|
||||
{
|
||||
name: "Website",
|
||||
link: "https://troplo.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Psittacus",
|
||||
internalName: "psittacus",
|
||||
tags: [
|
||||
{
|
||||
internalName: "development-halted",
|
||||
name: "Halted",
|
||||
icon: "mdi-pause-octagon",
|
||||
color: "warning"
|
||||
},
|
||||
{
|
||||
internalName: "vue",
|
||||
name: "Vue",
|
||||
icon: "mdi-vuejs",
|
||||
color: "#42b883",
|
||||
link: "https://vuejs.org"
|
||||
},
|
||||
{
|
||||
internalName: "tauri",
|
||||
name: "Tauri",
|
||||
icon: "mdi-infinity",
|
||||
color: "#24c8db",
|
||||
link: "https://tauri.studio"
|
||||
}
|
||||
],
|
||||
description: "Code editor. Coming soon.",
|
||||
visible: true,
|
||||
links: []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getVisible() {
|
||||
return this.projects.filter((i) => i.visible)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getImage(image) {
|
||||
try {
|
||||
return require("../assets/images/" + image + ".png")
|
||||
} catch {
|
||||
return (
|
||||
"https://dummyimage.com/3840x2035/151515/ffffff.png&text=" + image
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
7
src/vite-env.d.ts
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
14
troplo-website-next/components.d.ts
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
Header: typeof import('./src/components/Header.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
}
|
32
tsconfig.json
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"jsx": "preserve",
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"baseUrl": ".",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"resolveJsonModule": true,
|
||||
"types": [
|
||||
"vite/client",
|
||||
"unplugin-vue-router/client"
|
||||
],
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"src/**/*.vue"
|
||||
],
|
||||
"exclude": ["dist", "node_modules", "cypress"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }],
|
||||
}
|
9
tsconfig.node.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.mts"]
|
||||
}
|
27
typed-router.d.ts
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-router. ‼️ DO NOT MODIFY THIS FILE ‼️
|
||||
// It's recommended to commit this file.
|
||||
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry.
|
||||
|
||||
declare module 'vue-router/auto-routes' {
|
||||
import type {
|
||||
RouteRecordInfo,
|
||||
ParamValue,
|
||||
ParamValueOneOrMore,
|
||||
ParamValueZeroOrMore,
|
||||
ParamValueZeroOrOne,
|
||||
} from 'vue-router'
|
||||
|
||||
/**
|
||||
* Route name map generated by unplugin-vue-router
|
||||
*/
|
||||
export interface RouteNamedMap {
|
||||
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
|
||||
'/[...path]': RouteRecordInfo<'/[...path]', '/:path(.*)', { path: ParamValue<true> }, { path: ParamValue<false> }>,
|
||||
'/contact': RouteRecordInfo<'/contact', '/contact', Record<never, never>, Record<never, never>>,
|
||||
'/news/': RouteRecordInfo<'/news/', '/news', Record<never, never>, Record<never, never>>,
|
||||
'/news/[id]': RouteRecordInfo<'/news/[id]', '/news/:id', { id: ParamValue<true> }, { id: ParamValue<false> }>,
|
||||
}
|
||||
}
|
55
vite.config.mts
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Plugins
|
||||
import Components from "unplugin-vue-components/vite"
|
||||
import Vue from "@vitejs/plugin-vue"
|
||||
import Vuetify, { transformAssetUrls } from "vite-plugin-vuetify"
|
||||
import ViteFonts from "unplugin-fonts/vite"
|
||||
import VueRouter from "unplugin-vue-router/vite"
|
||||
|
||||
// Utilities
|
||||
import { defineConfig } from "vite"
|
||||
import { fileURLToPath, URL } from "node:url"
|
||||
import * as path from "node:path"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
VueRouter(),
|
||||
Vue({
|
||||
template: { transformAssetUrls }
|
||||
}),
|
||||
// https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme
|
||||
Vuetify({
|
||||
autoImport: true,
|
||||
styles: {
|
||||
configFile: "src/styles/settings.scss"
|
||||
}
|
||||
}),
|
||||
Components(),
|
||||
ViteFonts({
|
||||
google: {
|
||||
families: [
|
||||
{
|
||||
name: "Roboto",
|
||||
styles: "wght@100;300;400;500;700;900"
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
],
|
||||
define: { "process.env": {} },
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url))
|
||||
},
|
||||
extensions: [".js", ".json", ".jsx", ".mjs", ".ts", ".tsx", ".vue"]
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
"/graphql": {
|
||||
target: "http://localhost:24007",
|
||||
secure: false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
|
@ -1,38 +0,0 @@
|
|||
const WebpackAutoInject = require("webpack-auto-inject-version")
|
||||
module.exports = {
|
||||
transpileDependencies: ["vuetify"],
|
||||
productionSourceMap: false,
|
||||
configureWebpack: {
|
||||
plugins: [
|
||||
new WebpackAutoInject({
|
||||
SHORT: "Versioning",
|
||||
SILENT: false,
|
||||
PACKAGE_JSON_PATH: "./package.json",
|
||||
PACKAGE_JSON_INDENT: 2,
|
||||
components: {
|
||||
AutoIncreaseVersion: true,
|
||||
InjectAsComment: true,
|
||||
InjectByTag: true
|
||||
},
|
||||
componentsOptions: {
|
||||
AutoIncreaseVersion: {
|
||||
runInWatchMode: true
|
||||
},
|
||||
InjectAsComment: {
|
||||
tag: "Version information: {version}, Build Date: {date}",
|
||||
dateFormat: "dd/mm/yyyy; hh:MM:ss TT",
|
||||
multiLineCommentType: false
|
||||
},
|
||||
InjectByTag: {
|
||||
fileRegex: /\.+/,
|
||||
AIVTagRegexp: /(\[AIV])(([a-zA-Z{} ,:;!()_@\-"'\\\/])+)(\[\/AIV])/g,
|
||||
dateFormat: "dd/mm/yyyy; hh:MM:ss TT"
|
||||
}
|
||||
},
|
||||
LOGS_TEXT: {
|
||||
AIS_START: "AIV started"
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|