├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── enhancement.md ├── pull_request_template.md └── workflows │ ├── check-ags-lib-version.yml │ ├── publish-and-release-ags-lib.yml │ └── test-ags-lib.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── ags-validator-app ├── .eslintrc.json ├── .gitignore ├── README.md ├── app │ ├── api │ │ └── register │ │ │ └── route.ts │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ ├── page.tsx │ └── privacy │ │ └── page.tsx ├── components.json ├── components │ ├── NavBar.tsx │ ├── RegisterForm │ │ ├── RegisterForm.tsx │ │ └── index.tsx │ ├── ThemeProvider.tsx │ ├── ThemeSwitcher.tsx │ ├── logo │ │ ├── groundUpLogo.svg │ │ └── index.tsx │ ├── mobile-nav.tsx │ ├── ui │ │ ├── auto-complete.tsx │ │ ├── avatar.tsx │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── command.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── form.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── popover.tsx │ │ ├── separator.tsx │ │ ├── sheet.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ └── tooltip.tsx │ └── validator │ │ ├── AGSUpload │ │ ├── AGSUpload.tsx │ │ └── index.tsx │ │ ├── ErrorMessages │ │ ├── ErrorMessage.tsx │ │ ├── ErrorMessages.tsx │ │ ├── SortErrors.tsx │ │ └── index.tsx │ │ ├── GridView │ │ ├── GridView.tsx │ │ └── index.tsx │ │ ├── SelectTable │ │ ├── SelectTable.tsx │ │ └── index.tsx │ │ ├── TextArea │ │ ├── CodeMirrorTextArea.tsx │ │ └── index.tsx │ │ ├── ViewToolbar │ │ ├── ViewToolbar.tsx │ │ └── index.tsx │ │ ├── index.tsx │ │ ├── store-provider.tsx │ │ ├── validator-provider.tsx │ │ └── validator.tsx ├── hooks │ └── useGridTheme.ts ├── lib │ ├── redux │ │ ├── ags.ts │ │ ├── hooks.ts │ │ └── store.ts │ └── utils.ts ├── next.config.mjs ├── package.json ├── postcss.config.mjs ├── tailwind.config.ts ├── tsconfig.json └── workers │ ├── validateRawUpdateWorker.js │ └── validateRowUpdateWorker.js ├── ags ├── .gitignore ├── .npmignore ├── .prettierrc ├── __tests__ │ └── rules │ │ ├── rulesForParsedAgs │ │ ├── checkDataTypes.test.ts │ │ ├── checkGroupAndHeadings.test.ts │ │ ├── checkHeadingsWithDict.test.ts │ │ └── checkRequiredGroups.test.ts │ │ └── rulesForRawData.test.ts ├── assets │ ├── Standard_dictionary_v4_0_3.ags │ ├── Standard_dictionary_v4_0_3.ags.json │ ├── Standard_dictionary_v4_0_4.ags │ ├── Standard_dictionary_v4_0_4.ags.json │ ├── Standard_dictionary_v4_1.ags │ ├── Standard_dictionary_v4_1.ags.json │ ├── Standard_dictionary_v4_1_1.ags │ └── Standard_dictionary_v4_1_1.ags.json ├── eslint.config.mjs ├── jest.config.cjs ├── package.json ├── readme.md ├── rollup.config.js ├── scripts │ ├── buildDictionaries.ts │ └── profile.cjs ├── src │ ├── index.ts │ ├── parse.ts │ ├── rules │ │ ├── index.ts │ │ ├── rulesForParsedAgs │ │ │ ├── checkDataTypes.ts │ │ │ ├── checkGroupAndHeadings.ts │ │ │ ├── checkHeadingsWithDict.ts │ │ │ ├── checkRequiredGroups.ts │ │ │ ├── index.ts │ │ │ └── types.d.ts │ │ ├── rulesForRawData.ts │ │ └── types.d.ts │ ├── standardDictionaries.ts │ ├── types.ts │ └── validate.ts └── tsconfig.json ├── package-lock.json └── package.json /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a problem or bug in the application 4 | title: "[BUG] Brief description of the bug" 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | **Describe the Bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | e.g. 16 | 17 | 1. Go to '...' 18 | 2. Click on '...' 19 | 3. Scroll down to '...' 20 | 4. See error 21 | 22 | **Expected Behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **Desktop (please complete the following information):** 29 | - OS: [e.g., macOS, Windows] 30 | - Browser: [e.g., Chrome, Firefox] 31 | 32 | **Smartphone (please complete the following information):** 33 | - Device: [e.g., iPhone 12] 34 | - OS: [e.g., iOS 15] 35 | - Browser: [e.g., Safari] 36 | - Version: [e.g., 15] 37 | 38 | **Additional Context** 39 | Add any other context about the problem here. 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement Request 3 | about: Suggest an idea for improving the application 4 | title: "[ENHANCEMENT] Brief description of the enhancement" 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | **Is Your Enhancement Related to a Problem? Please Describe.** 10 | A clear and concise description of the problem or need. 11 | 12 | **Describe the Solution You'd Like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe Alternatives You've Considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional Context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | Please include a summary of the change and which issue is fixed. List any dependencies that are required for this change. 4 | 5 | Please link and relevant issues. 6 | 7 | ### Type of Change 8 | 9 | Please delete options that are not relevant. 10 | 11 | - [ ] Bug fix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | - [ ] Documentation update 15 | 16 | ### Checklist 17 | 18 | - [ ] I have performed a self-review of my own code 19 | - [ ] I have commented my code, particularly in hard-to-understand areas 20 | - [ ] I have made corresponding changes to the documentation 21 | - [ ] My changes generate no new warnings 22 | - [ ] I have added tests that prove my fix is effective or that my feature works 23 | - [ ] New and existing unit tests pass locally with my changes 24 | 25 | ### Screenshots (if applicable) 26 | 27 | Add screenshots to help explain the changes (if applicable). 28 | 29 | ### Additional Context 30 | 31 | Add any other context or screenshots about the pull request here. 32 | -------------------------------------------------------------------------------- /.github/workflows/check-ags-lib-version.yml: -------------------------------------------------------------------------------- 1 | name: Check AGS Package Version 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'ags/**' # Trigger only if files in the `ags` package have changed 7 | 8 | jobs: 9 | check_version: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v3 14 | 15 | - name: Get current branch version of ags package 16 | id: current_version 17 | run: | 18 | jq -r '.version' ags/package.json > current_version.txt 19 | echo "current_version=$(cat current_version.txt)" >> $GITHUB_ENV 20 | 21 | - name: Get main branch version of ags package 22 | run: | 23 | git fetch origin main 24 | git show origin/main:ags/package.json | jq -r '.version' > main_version.txt 25 | echo "main_version=$(cat main_version.txt)" >> $GITHUB_ENV 26 | 27 | - name: Check for changes in the ags package 28 | id: check_changes 29 | run: | 30 | if git diff --quiet origin/main -- ags/; then 31 | echo "has_changes=false" >> $GITHUB_ENV 32 | else 33 | echo "has_changes=true" >> $GITHUB_ENV 34 | fi 35 | 36 | - name: Fail if version is not updated 37 | if: env.has_changes == 'true' && env.current_version == env.main_version 38 | run: | 39 | echo "Code has changed in the ags package, but the version has not been updated." 40 | exit 1 41 | env: 42 | current_version: ${{ env.current_version }} 43 | main_version: ${{ env.main_version }} 44 | -------------------------------------------------------------------------------- /.github/workflows/publish-and-release-ags-lib.yml: -------------------------------------------------------------------------------- 1 | name: Publish and Release AGS Package 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'ags/**' 9 | - '.github/workflows/publish-and-release-ags-lib.yml' 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: read 15 | id-token: write 16 | steps: 17 | - uses: actions/checkout@v4 18 | # Setup .npmrc file to publish to npm 19 | - uses: actions/setup-node@v4 20 | with: 21 | node-version: '20.x' 22 | registry-url: 'https://registry.npmjs.org' 23 | - run: npm install --workspace ags 24 | - run: npm publish --workspace ags 25 | env: 26 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 27 | 28 | -------------------------------------------------------------------------------- /.github/workflows/test-ags-lib.yml: -------------------------------------------------------------------------------- 1 | name: Run Jest Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [18.x, 20.x] # Test on multiple versions of Node.js 18 | 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v3 22 | 23 | - name: Set up Node.js 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | cache: 'npm' 28 | 29 | # Use a working directory for the rest of the steps 30 | - name: Install dependencies 31 | run: npm install 32 | working-directory: ./ags # Set the working directory 33 | 34 | - name: Run Jest Tests 35 | run: npm run test 36 | working-directory: ./ags # Set the working directory 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **node_modules 2 | package-lock.json 3 | 4 | **.env -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 21 | [![Contributors][contributors-shield]][contributors-url] 22 | [![Forks][forks-shield]][forks-url] 23 | [![Stargazers][stars-shield]][stars-url] 24 | [![Issues][issues-shield]][issues-url] 25 | 26 | 27 | 28 |
29 |
30 | 31 | Logo 32 | 33 | 34 |

AGS Validator and Editor

35 | 36 |

37 | By GroundUp 38 |
39 |
40 |
41 | Report Bug 42 | · 43 | Request Feature 44 |

45 |
46 | 47 | 48 | 49 | 50 |
51 | Table of Contents 52 |
    53 |
  1. 54 | About The Project 55 |
  2. 56 | 57 |
  3. Contributing
  4. 58 |
  5. License
  6. 59 |
60 |
61 | 62 | 63 | 64 | 65 | ## About The Project 66 | 67 | Validate, fix, and edit your AGS data with ease. 68 | All your AGS data stays on your device and never leaves your browser. 69 | 70 | This repository is home to two typescript projects: 71 | 72 | ### @groundup/ags 73 | 74 | This is the npm package that performs parsing and validation of AGS4 files. 75 | Source code and docs are available [here](https://github.com/groundup-dev/ags-validator/tree/main/ags). 76 | 77 | ### Ags Editor Web App 78 | 79 | This is the web application that lives [here](https://validator.groundup.cloud). 80 | 81 | Source code is available [here](https://github.com/groundup-dev/ags-validator/tree/main/ags-validator-app). 82 | 83 |

(back to top)

84 | 85 | 86 | ## What is GroundUp? 87 | 88 | GroundUp is an open source platform that makes geotechnical analysis and data management, easier, quicker, more transparent, and more collaborative. 89 | 90 | We are currently working hard on building the public version of GroundUp, which will be available soon. 91 | 92 | Register for early access to GroundUp and be the first to know when it's available. 93 | 94 | GroundUp will **always be open-source, and free to get started**. 95 | 96 | 97 | 98 | 99 | ## Contributing 100 | 101 | Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. 102 | 103 | You don't have to write code to contribute. Creating issues, providing feedback, and writing documentation are all invaluable contributions. 104 | 105 | If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". 106 | Don't forget to give the project a star! Thanks again! 107 | 108 | 1. Fork the Project 109 | 2. Create your Feature Branch (`git checkout -b feat/AmazingFeature`) 110 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 111 | 4. Push to the Branch (`git push origin feat/AmazingFeature`) 112 | 5. Open a Pull Request 113 | 114 |

(back to top)

115 | 116 | ### Top contributors: 117 | 118 | 119 | contrib.rocks image 120 | 121 | 122 | 123 | ## License 124 | 125 | Distributed under the Apache v2 License. See `LICENSE.txt` for more information. 126 | 127 |

(back to top)

128 | 129 | 130 | 131 |

(back to top)

132 | 133 | 134 | 135 | 136 | 137 | [contributors-shield]: https://img.shields.io/github/contributors/groundup-dev/ags-validator.svg?style=for-the-badge 138 | [contributors-url]: https://github.com/groundup-dev/ags-validator/graphs/contributors 139 | [forks-shield]: https://img.shields.io/github/forks/groundup-dev/ags-validator.svg?style=for-the-badge 140 | [forks-url]: https://github.com/groundup-dev/ags-validator/network/members 141 | [stars-shield]: https://img.shields.io/github/stars/groundup-dev/ags-validator.svg?style=for-the-badge 142 | [stars-url]: https://github.com/groundup-dev/ags-validator/stargazers 143 | [issues-shield]: https://img.shields.io/github/issues/groundup-dev/ags-validator.svg?style=for-the-badge 144 | [issues-url]: https://github.com/groundup-dev/ags-validator/issues 145 | [license-shield]: https://img.shields.io/github/license/groundup-dev/ags-validator.svg?style=for-the-badge 146 | [license-url]: https://github.com/groundup-dev/ags-validator/blob/main/LICENSE.txt 147 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 148 | [linkedin-url]: https://linkedin.com/in/linkedin_username 149 | [product-screenshot]: images/screenshot.png 150 | [Next.js]: https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white 151 | [Next-url]: https://nextjs.org/ 152 | [React.js]: https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB 153 | [React-url]: https://reactjs.org/ 154 | [Vue.js]: https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge&logo=vuedotjs&logoColor=4FC08D 155 | [Vue-url]: https://vuejs.org/ 156 | [Angular.io]: https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular&logoColor=white 157 | [Angular-url]: https://angular.io/ 158 | [Svelte.dev]: https://img.shields.io/badge/Svelte-4A4A55?style=for-the-badge&logo=svelte&logoColor=FF3E00 159 | [Svelte-url]: https://svelte.dev/ 160 | [Laravel.com]: https://img.shields.io/badge/Laravel-FF2D20?style=for-the-badge&logo=laravel&logoColor=white 161 | [Laravel-url]: https://laravel.com 162 | [Bootstrap.com]: https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge&logo=bootstrap&logoColor=white 163 | [Bootstrap-url]: https://getbootstrap.com 164 | [JQuery.com]: https://img.shields.io/badge/jQuery-0769AD?style=for-the-badge&logo=jquery&logoColor=white 165 | [JQuery-url]: https://jquery.com -------------------------------------------------------------------------------- /ags-validator-app/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "next/typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /ags-validator-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /ags-validator-app/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | 10 | ``` 11 | 12 | -------------------------------------------------------------------------------- /ags-validator-app/app/api/register/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | 3 | // Mailchimp API URL template 4 | const MAILCHIMP_API_URL = `https://${process.env.MAILCHIMP_SERVER_PREFIX}.api.mailchimp.com/3.0`; 5 | 6 | export async function POST(req: Request) { 7 | const { email, firstName, lastName } = await req.json(); 8 | 9 | const audienceId = process.env.MAILCHIMP_AUDIENCE_ID; 10 | const apiKey = process.env.MAILCHIMP_API_KEY; 11 | 12 | if (!audienceId) { 13 | return NextResponse.json({ status: 500 }); 14 | } 15 | 16 | if (!email || !firstName || !lastName) { 17 | return NextResponse.json( 18 | { error: "Email and name are required" }, 19 | { status: 400 } 20 | ); 21 | } 22 | 23 | // Construct the Mailchimp API URL 24 | const url = `${MAILCHIMP_API_URL}/lists/${audienceId}/members`; 25 | 26 | // Mailchimp requires the API key to be passed as a Base64-encoded string 27 | const auth = Buffer.from(`anystring:${apiKey}`).toString("base64"); 28 | 29 | // Make the POST request to Mailchimp API 30 | const response = await fetch(url, { 31 | method: "POST", 32 | headers: { 33 | Authorization: `Basic ${auth}`, 34 | "Content-Type": "application/json", 35 | }, 36 | body: JSON.stringify({ 37 | email_address: email, 38 | status: "subscribed", 39 | merge_fields: { 40 | FNAME: firstName, 41 | LNAME: lastName, 42 | }, 43 | }), 44 | }); 45 | 46 | if (!response.ok) { 47 | const errorData = await response.json(); 48 | 49 | if (errorData.title === "Member Exists") { 50 | return NextResponse.json( 51 | { error: "User is already subscribed" }, 52 | { status: response.status } 53 | ); 54 | } 55 | return NextResponse.json( 56 | { error: "Failed to subscribe user" }, 57 | { status: response.status } 58 | ); 59 | } 60 | 61 | return NextResponse.json({ 62 | message: "User subscribed successfully", 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /ags-validator-app/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundup-dev/ags-validator/46efe868d8231e960f29d14507d861cbc8487e95/ags-validator-app/app/favicon.ico -------------------------------------------------------------------------------- /ags-validator-app/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | 6 | 7 | @layer base { 8 | :root { 9 | --background: 0 0% 100%; 10 | --foreground: 0 0% 0%; 11 | --card: 0 0% 100%; 12 | --card-foreground: 0 0% 0%; 13 | --popover: 0 0% 100%; 14 | --popover-foreground: 0 0% 0%; 15 | --primary: 130 100% 25%; 16 | --primary-foreground: 0 0% 100%; 17 | --secondary: 0 0% 94%; 18 | --secondary-foreground: 0 0% 0%; 19 | --alternative: 30 100% 30%; 20 | --alternative-foreground: 0 0% 100%; 21 | --muted: 207 86 95%; 22 | --muted-foreground: 0 0% 0%; 23 | --accent: 0 0% 96%; 24 | --accent-foreground: 0 0% 0%; 25 | --destructive: 11 93% 55%; 26 | --destructive-foreground: 11 93% 85%; 27 | --warning: 39 97% 50%; 28 | --warning-foreground: 39 97% 83%; 29 | --border: 0 0% 75%; 30 | --input: 0 0% 81%; 31 | --ring: 0 0% 100% / 0; 32 | --success: 140 100% 32%; 33 | --radius: 0.5rem; 34 | 35 | } 36 | 37 | .dark { 38 | --background: 0 0% 7%; 39 | --foreground: 0 0% 98%; 40 | --card: 0 0% 9%; 41 | --card-foreground: 0 0% 98%; 42 | --popover: 0 0% 9%; 43 | --popover-foreground: 0 0% 98%; 44 | --primary: 130 100% 25%; 45 | --primary-foreground: 0 0% 100%; 46 | --secondary: 0 0% 15%; 47 | --secondary-foreground: 0 0% 98%; 48 | --alternative: 30 100% 30%; 49 | --alternative-foreground: 0 0% 100%; 50 | --muted: 207 90 3%; 51 | --muted-foreground: 0 0% 85%; 52 | --accent: 0 0% 20%; 53 | --accent-foreground: 0 0% 98%; 54 | --destructive: 11 93% 45%; 55 | --destructive-foreground: 11 93% 95%; 56 | --warning: 39 97% 40%; 57 | --warning-foreground: 39 97% 93%; 58 | --border: 0 0% 25%; 59 | --input: 0 0% 25%; 60 | --warning: 39 97% 50%; 61 | --warning-foreground: 39 97% 83%; 62 | 63 | --success: 140 100% 42%; 64 | } 65 | } 66 | 67 | @layer base { 68 | * { 69 | @apply border-border; 70 | 71 | } 72 | body { 73 | @apply bg-background text-foreground; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ags-validator-app/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import { Analytics } from "@vercel/analytics/react"; 4 | import { ThemeProvider } from "@/components/ThemeProvider"; 5 | const inter = Inter({ 6 | subsets: ["latin"], 7 | variable: "--font-inter", 8 | }); 9 | 10 | import "./globals.css"; 11 | import React from "react"; 12 | import Navbar from "@/components/NavBar"; 13 | 14 | export const metadata: Metadata = { 15 | title: "GroundUp | AGS4 Editor", 16 | description: "AGS4 Editor by GroundUp", 17 | }; 18 | 19 | export default function RootLayout({ 20 | children, 21 | }: Readonly<{ 22 | children: React.ReactNode; 23 | }>) { 24 | return ( 25 | 26 | 27 | 30 | 36 | 37 |
{children}
38 | 39 |
40 | 41 | 42 | 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /ags-validator-app/app/page.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { 4 | Dialog, 5 | DialogTrigger, 6 | DialogContent, 7 | DialogHeader, 8 | DialogTitle, 9 | DialogDescription, 10 | } from "@/components/ui/dialog"; 11 | import { 12 | CheckSquare, 13 | CornerLeftDown, 14 | CornerRightDown, 15 | DownloadCloud, 16 | Edit, 17 | ExternalLink, 18 | HelpCircle, 19 | UploadCloudIcon, 20 | } from "lucide-react"; 21 | import Validator from "@/components/validator"; 22 | import { Button } from "@/components/ui/button"; 23 | import Link from "next/link"; 24 | import { Logo } from "@/components/logo"; 25 | 26 | export default function Page() { 27 | return ( 28 |
29 |
30 |
31 |
32 |

AGS Editor

33 |
34 |

by

35 | 36 |
37 |
38 |
39 |

40 | Edit and check your AGS data with ease. 41 |

42 |

43 | All your AGS data stays on your device and 44 | never leaves your browser. 45 |

46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Data Privacy 55 | 56 | 57 | Your AGS files are processed entirely within your browser. 58 | This means that your data never leaves your device, and 59 | you can use this tool without worrying about any data 60 | privacy and residency requirements. 61 | 62 | 63 | 64 | 65 |

66 |
67 | 68 |
69 |
70 |
71 |
72 |
73 | 74 | Upload 75 |
76 |

77 | Upload or paste your AGS data using the tools below 78 |

79 |
80 | 84 |
85 | 86 |
87 | 91 |
92 |
93 | 94 | Validate 95 |
96 |

97 | Validate your data against any AGS4 version, and inspect 98 | issues 99 |

100 |
101 |
102 | 103 |
104 |
105 |
106 | 107 | Edit 108 |
109 |

Edit your data in table or text view

110 |
111 | 115 |
116 | 117 |
118 |
119 |
120 |
121 | 122 | Export 123 |
124 |

Export your data as AGS4

125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |

134 | Register for{" "} 135 | 136 | early access 137 | 138 |

139 |

140 | We're currently in closed beta, so join the waitlist to get 141 | early access, and help shape GroundUp. 142 |

143 |
144 | 152 |
153 |
154 |
155 |
156 | 157 |
158 |
159 | ); 160 | } 161 | -------------------------------------------------------------------------------- /ags-validator-app/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /ags-validator-app/components/NavBar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { Logo } from "@/components/logo"; 5 | 6 | import { FaGithub } from "react-icons/fa"; 7 | import Link from "next/link"; 8 | import { 9 | DropdownMenu, 10 | DropdownMenuContent, 11 | DropdownMenuItem, 12 | DropdownMenuLabel, 13 | DropdownMenuSeparator, 14 | DropdownMenuTrigger, 15 | } from "./ui/dropdown-menu"; 16 | import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; 17 | import { Separator } from "./ui/separator"; 18 | import { ExternalLink } from "lucide-react"; 19 | import { ThemeSwitcher } from "./ThemeSwitcher"; 20 | import { MobileNav } from "./mobile-nav"; 21 | import { Button } from "./ui/button"; 22 | 23 | export default function Navbar() { 24 | return ( 25 |
26 |
27 | 28 |
29 | 30 | 33 | 34 |
35 |

We are always happy to chat.

36 |

Feel free to reach out to us at

37 | 38 |

hello@groundup.cloud

39 |
40 |
41 |
42 | 43 | 44 | 47 | 48 | 49 | Feedback 50 | 51 | 52 | 57 | Report an issue 58 | 59 | 60 | 61 | 62 | 67 | Suggest a change 68 | 69 | 70 | 71 | 72 | 73 |
74 | 75 |
76 | 77 |
78 | 84 | 90 |
91 | 92 |
93 |
94 |
95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /ags-validator-app/components/RegisterForm/RegisterForm.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { z } from "zod"; 4 | import { zodResolver } from "@hookform/resolvers/zod"; 5 | import { useForm } from "react-hook-form"; 6 | 7 | import { Button } from "@/components/ui/button"; 8 | import { 9 | Form, 10 | FormControl, 11 | FormField, 12 | FormItem, 13 | FormLabel, 14 | FormMessage, 15 | } from "@/components/ui/form"; 16 | import { Input } from "@/components/ui/input"; 17 | import { Check, LoaderCircle } from "lucide-react"; 18 | import { useState } from "react"; 19 | import { track } from "@vercel/analytics"; 20 | 21 | const formSchema = z.object({ 22 | firstName: z.string().min(1, "First name is required"), 23 | lastName: z.string().min(1, "Last name is required"), 24 | email: z.string().email("Invalid email address"), 25 | }); 26 | 27 | export function RegisterForm() { 28 | const [isLoading, setIsLoading] = useState(false); 29 | const [isSuccess, setIsSuccess] = useState(false); 30 | const [error, setError] = useState(null); 31 | 32 | const form = useForm>({ 33 | resolver: zodResolver(formSchema), 34 | defaultValues: { 35 | firstName: "", 36 | lastName: "", 37 | email: "", 38 | }, 39 | }); 40 | 41 | async function onSubmit(values: z.infer) { 42 | setIsLoading(true); 43 | setError(null); 44 | const response = await fetch("/api/register", { 45 | method: "POST", 46 | body: JSON.stringify(values), 47 | }); 48 | setIsLoading(false); 49 | 50 | if (!response.ok) { 51 | console.error("Failed to submit form"); 52 | const data = await response.json(); 53 | setError(data.error); 54 | track("failed-registration", { error: data.error }); 55 | 56 | return; 57 | } else { 58 | track("completed-registration"); 59 | setIsSuccess(true); 60 | } 61 | } 62 | 63 | return ( 64 |
65 | 66 |
67 | ( 71 | 72 | First Name 73 | 74 | 75 | 76 | 77 | 78 | 79 | )} 80 | /> 81 | ( 85 | 86 | Last Name 87 | 88 | 89 | 90 | 91 | 92 | 93 | )} 94 | /> 95 |
96 | ( 100 | 101 | Email 102 | 103 | 104 | 105 | 106 | 107 | 108 | )} 109 | /> 110 |
111 | {!isLoading && !isSuccess && ( 112 | 119 | )} 120 | {isSuccess && ( 121 | 122 | 123 | Success! We will contact you when GroundUp is ready. 124 | 125 | )} 126 | 127 | {isLoading && } 128 | {error && {error}} 129 |
130 | 131 | 132 | ); 133 | } 134 | -------------------------------------------------------------------------------- /ags-validator-app/components/RegisterForm/index.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundup-dev/ags-validator/46efe868d8231e960f29d14507d861cbc8487e95/ags-validator-app/components/RegisterForm/index.tsx -------------------------------------------------------------------------------- /ags-validator-app/components/ThemeProvider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { 5 | ThemeProvider as NextThemesProvider, 6 | ThemeProviderProps, 7 | } from "next-themes"; 8 | 9 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 10 | return {children}; 11 | } 12 | -------------------------------------------------------------------------------- /ags-validator-app/components/ThemeSwitcher.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { Moon, Sun, Laptop } from "lucide-react"; 5 | import { useTheme } from "next-themes"; 6 | 7 | import { Button } from "@/components/ui/button"; 8 | import { 9 | DropdownMenu, 10 | DropdownMenuContent, 11 | DropdownMenuItem, 12 | DropdownMenuTrigger, 13 | } from "@/components/ui/dropdown-menu"; 14 | 15 | export function ThemeSwitcher() { 16 | const { setTheme } = useTheme(); 17 | 18 | return ( 19 | 20 | 21 | 26 | 27 | 28 | setTheme("light")}> 29 | 30 | Light 31 | 32 | setTheme("dark")}> 33 | 34 | Dark 35 | 36 | setTheme("system")}> 37 | 38 | System 39 | 40 | 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /ags-validator-app/components/logo/groundUpLogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /ags-validator-app/components/logo/index.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | import Image from "next/image"; 3 | import groundUpLogo from "./groundUpLogo.svg"; 4 | 5 | const sizeClassNames = { 6 | sm: { logo: "h-8", text: "text-md" }, 7 | md: { logo: "h-10", text: "text-lg" }, 8 | lg: { logo: "h-12", text: "text-xl" }, 9 | }; 10 | 11 | interface Props { 12 | size: keyof typeof sizeClassNames; 13 | showText?: boolean; 14 | } 15 | 16 | export function Logo({ size, showText = true }: Props) { 17 | const heights = { 18 | sm: 32, // h-8 19 | md: 40, // h-10 20 | lg: 48, // h-12 21 | }; 22 | 23 | return ( 24 |
25 | GroundUp logo 32 | {showText && ( 33 |

34 | GroundUp 35 |

36 | )} 37 |
38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /ags-validator-app/components/mobile-nav.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@/components/ui/button"; 4 | import { 5 | Sheet, 6 | SheetContent, 7 | SheetHeader, 8 | SheetTitle, 9 | SheetTrigger, 10 | } from "@/components/ui/sheet"; 11 | import { ExternalLink, Menu } from "lucide-react"; 12 | import Link from "next/link"; 13 | import { FaGithub } from "react-icons/fa"; 14 | import { useState } from "react"; 15 | import { Separator } from "./ui/separator"; 16 | 17 | export function MobileNav() { 18 | const [open, setOpen] = useState(false); 19 | 20 | return ( 21 | 22 | 23 | 30 | 31 | 32 | 33 | Menu 34 | 35 | 99 | 100 | 101 | ); 102 | } 103 | -------------------------------------------------------------------------------- /ags-validator-app/components/ui/auto-complete.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { Check, ChevronsUpDown } from "lucide-react"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | import { Button } from "@/components/ui/button"; 8 | import { 9 | Command, 10 | CommandEmpty, 11 | CommandGroup, 12 | CommandInput, 13 | CommandItem, 14 | CommandList, 15 | } from "@/components/ui/command"; 16 | import { 17 | Popover, 18 | PopoverContent, 19 | PopoverTrigger, 20 | } from "@/components/ui/popover"; 21 | import { Label } from "@/components/ui/label"; 22 | 23 | type Props = { 24 | options: { value: string; label: string }[]; 25 | label: string; 26 | placeholder?: string; 27 | selectedOption: string; 28 | setSelectedOption: React.Dispatch>; 29 | }; 30 | 31 | export default function AutoComplete({ 32 | options, 33 | selectedOption, 34 | setSelectedOption, 35 | label, 36 | placeholder, 37 | }: Props) { 38 | const [open, setOpen] = React.useState(false); 39 | 40 | return ( 41 |
42 | 43 | 44 | 45 | 51 | 52 | 53 | 54 | 55 | 56 | No option found. 57 | 58 | {options.map((option) => ( 59 | { 63 | setSelectedOption(currentValue); 64 | setOpen(false); 65 | }} 66 | > 67 | 75 | {option.label} 76 | 77 | ))} 78 | 79 | 80 | 81 | 82 | 83 |
84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /ags-validator-app/components/ui/avatar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as AvatarPrimitive from "@radix-ui/react-avatar"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | const Avatar = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | )); 21 | Avatar.displayName = AvatarPrimitive.Root.displayName; 22 | 23 | const AvatarImage = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, ...props }, ref) => ( 27 | 32 | )); 33 | AvatarImage.displayName = AvatarPrimitive.Image.displayName; 34 | 35 | const AvatarFallback = React.forwardRef< 36 | React.ElementRef, 37 | React.ComponentPropsWithoutRef 38 | >(({ className, ...props }, ref) => ( 39 | 47 | )); 48 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; 49 | 50 | export { Avatar, AvatarImage, AvatarFallback }; 51 | -------------------------------------------------------------------------------- /ags-validator-app/components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cva, type VariantProps } from "class-variance-authority"; 3 | 4 | import { cn } from "@/lib/utils"; 5 | 6 | const badgeVariants = cva( 7 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", 8 | { 9 | variants: { 10 | variant: { 11 | default: 12 | "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", 13 | secondary: 14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 15 | destructive: 16 | "border-transparent bg-destructive text-white hover:bg-destructive/80", 17 | outline: "text-foreground", 18 | warning: "border-transparent bg-warning text-white hover:bg-warning/80", 19 | }, 20 | }, 21 | defaultVariants: { 22 | variant: "default", 23 | }, 24 | } 25 | ); 26 | 27 | export interface BadgeProps 28 | extends React.HTMLAttributes, 29 | VariantProps {} 30 | 31 | function Badge({ className, variant, ...props }: BadgeProps) { 32 | return ( 33 |
34 | ); 35 | } 36 | 37 | export { Badge, badgeVariants }; 38 | -------------------------------------------------------------------------------- /ags-validator-app/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Slot } from "@radix-ui/react-slot"; 3 | import { cva, type VariantProps } from "class-variance-authority"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", 9 | { 10 | variants: { 11 | variant: { 12 | default: "bg-primary text-primary-foreground hover:bg-primary/90", 13 | destructive: 14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90", 15 | outline: 16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 17 | secondary: 18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80", 19 | ghost: "hover:bg-accent hover:text-accent-foreground", 20 | link: "text-primary underline-offset-4 hover:underline", 21 | }, 22 | size: { 23 | default: "h-10 px-4 py-2", 24 | sm: "h-9 rounded-md px-3", 25 | lg: "h-11 rounded-md px-8", 26 | icon: "h-9 w-9", 27 | }, 28 | }, 29 | defaultVariants: { 30 | variant: "default", 31 | size: "default", 32 | }, 33 | } 34 | ); 35 | 36 | export interface ButtonProps 37 | extends React.ButtonHTMLAttributes, 38 | VariantProps { 39 | asChild?: boolean; 40 | } 41 | 42 | const Button = React.forwardRef( 43 | ({ className, variant, size, asChild = false, ...props }, ref) => { 44 | const Comp = asChild ? Slot : "button"; 45 | return ( 46 | 51 | ); 52 | } 53 | ); 54 | Button.displayName = "Button"; 55 | 56 | export { Button, buttonVariants }; 57 | -------------------------------------------------------------------------------- /ags-validator-app/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = "Card" 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = "CardHeader" 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

44 | )) 45 | CardTitle.displayName = "CardTitle" 46 | 47 | const CardDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |

56 | )) 57 | CardDescription.displayName = "CardDescription" 58 | 59 | const CardContent = React.forwardRef< 60 | HTMLDivElement, 61 | React.HTMLAttributes 62 | >(({ className, ...props }, ref) => ( 63 |

64 | )) 65 | CardContent.displayName = "CardContent" 66 | 67 | const CardFooter = React.forwardRef< 68 | HTMLDivElement, 69 | React.HTMLAttributes 70 | >(({ className, ...props }, ref) => ( 71 |
76 | )) 77 | CardFooter.displayName = "CardFooter" 78 | 79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 80 | -------------------------------------------------------------------------------- /ags-validator-app/components/ui/command.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import { type DialogProps } from "@radix-ui/react-dialog" 5 | import { Command as CommandPrimitive } from "cmdk" 6 | import { Search } from "lucide-react" 7 | 8 | import { cn } from "@/lib/utils" 9 | import { Dialog, DialogContent } from "@/components/ui/dialog" 10 | 11 | const Command = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef 14 | >(({ className, ...props }, ref) => ( 15 | 23 | )) 24 | Command.displayName = CommandPrimitive.displayName 25 | 26 | interface CommandDialogProps extends DialogProps {} 27 | 28 | const CommandDialog = ({ children, ...props }: CommandDialogProps) => { 29 | return ( 30 | 31 | 32 | 33 | {children} 34 | 35 | 36 | 37 | ) 38 | } 39 | 40 | const CommandInput = React.forwardRef< 41 | React.ElementRef, 42 | React.ComponentPropsWithoutRef 43 | >(({ className, ...props }, ref) => ( 44 |
45 | 46 | 54 |
55 | )) 56 | 57 | CommandInput.displayName = CommandPrimitive.Input.displayName 58 | 59 | const CommandList = React.forwardRef< 60 | React.ElementRef, 61 | React.ComponentPropsWithoutRef 62 | >(({ className, ...props }, ref) => ( 63 | 68 | )) 69 | 70 | CommandList.displayName = CommandPrimitive.List.displayName 71 | 72 | const CommandEmpty = React.forwardRef< 73 | React.ElementRef, 74 | React.ComponentPropsWithoutRef 75 | >((props, ref) => ( 76 | 81 | )) 82 | 83 | CommandEmpty.displayName = CommandPrimitive.Empty.displayName 84 | 85 | const CommandGroup = React.forwardRef< 86 | React.ElementRef, 87 | React.ComponentPropsWithoutRef 88 | >(({ className, ...props }, ref) => ( 89 | 97 | )) 98 | 99 | CommandGroup.displayName = CommandPrimitive.Group.displayName 100 | 101 | const CommandSeparator = React.forwardRef< 102 | React.ElementRef, 103 | React.ComponentPropsWithoutRef 104 | >(({ className, ...props }, ref) => ( 105 | 110 | )) 111 | CommandSeparator.displayName = CommandPrimitive.Separator.displayName 112 | 113 | const CommandItem = React.forwardRef< 114 | React.ElementRef, 115 | React.ComponentPropsWithoutRef 116 | >(({ className, ...props }, ref) => ( 117 | 125 | )) 126 | 127 | CommandItem.displayName = CommandPrimitive.Item.displayName 128 | 129 | const CommandShortcut = ({ 130 | className, 131 | ...props 132 | }: React.HTMLAttributes) => { 133 | return ( 134 | 141 | ) 142 | } 143 | CommandShortcut.displayName = "CommandShortcut" 144 | 145 | export { 146 | Command, 147 | CommandDialog, 148 | CommandInput, 149 | CommandList, 150 | CommandEmpty, 151 | CommandGroup, 152 | CommandItem, 153 | CommandShortcut, 154 | CommandSeparator, 155 | } 156 | -------------------------------------------------------------------------------- /ags-validator-app/components/ui/dialog.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as DialogPrimitive from "@radix-ui/react-dialog" 5 | import { X } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Dialog = DialogPrimitive.Root 10 | 11 | const DialogTrigger = DialogPrimitive.Trigger 12 | 13 | const DialogPortal = DialogPrimitive.Portal 14 | 15 | const DialogClose = DialogPrimitive.Close 16 | 17 | const DialogOverlay = React.forwardRef< 18 | React.ElementRef, 19 | React.ComponentPropsWithoutRef 20 | >(({ className, ...props }, ref) => ( 21 | 29 | )) 30 | DialogOverlay.displayName = DialogPrimitive.Overlay.displayName 31 | 32 | const DialogContent = React.forwardRef< 33 | React.ElementRef, 34 | React.ComponentPropsWithoutRef 35 | >(({ className, children, ...props }, ref) => ( 36 | 37 | 38 | 46 | {children} 47 | 48 | 49 | Close 50 | 51 | 52 | 53 | )) 54 | DialogContent.displayName = DialogPrimitive.Content.displayName 55 | 56 | const DialogHeader = ({ 57 | className, 58 | ...props 59 | }: React.HTMLAttributes) => ( 60 |
67 | ) 68 | DialogHeader.displayName = "DialogHeader" 69 | 70 | const DialogFooter = ({ 71 | className, 72 | ...props 73 | }: React.HTMLAttributes) => ( 74 |
81 | ) 82 | DialogFooter.displayName = "DialogFooter" 83 | 84 | const DialogTitle = React.forwardRef< 85 | React.ElementRef, 86 | React.ComponentPropsWithoutRef 87 | >(({ className, ...props }, ref) => ( 88 | 96 | )) 97 | DialogTitle.displayName = DialogPrimitive.Title.displayName 98 | 99 | const DialogDescription = React.forwardRef< 100 | React.ElementRef, 101 | React.ComponentPropsWithoutRef 102 | >(({ className, ...props }, ref) => ( 103 | 108 | )) 109 | DialogDescription.displayName = DialogPrimitive.Description.displayName 110 | 111 | export { 112 | Dialog, 113 | DialogPortal, 114 | DialogOverlay, 115 | DialogClose, 116 | DialogTrigger, 117 | DialogContent, 118 | DialogHeader, 119 | DialogFooter, 120 | DialogTitle, 121 | DialogDescription, 122 | } 123 | -------------------------------------------------------------------------------- /ags-validator-app/components/ui/dropdown-menu.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | 5 | import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; 6 | import { Check, ChevronRight, Circle } from "lucide-react"; 7 | 8 | import { cn } from "@/lib/utils"; 9 | 10 | const DropdownMenu = DropdownMenuPrimitive.Root; 11 | 12 | const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; 13 | 14 | const DropdownMenuGroup = DropdownMenuPrimitive.Group; 15 | 16 | const DropdownMenuPortal = DropdownMenuPrimitive.Portal; 17 | 18 | const DropdownMenuSub = DropdownMenuPrimitive.Sub; 19 | 20 | const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; 21 | 22 | const DropdownMenuSubTrigger = React.forwardRef< 23 | React.ElementRef, 24 | React.ComponentPropsWithoutRef & { 25 | inset?: boolean; 26 | } 27 | >(({ className, inset, children, ...props }, ref) => ( 28 | 37 | {children} 38 | 39 | 40 | )); 41 | DropdownMenuSubTrigger.displayName = 42 | DropdownMenuPrimitive.SubTrigger.displayName; 43 | 44 | const DropdownMenuSubContent = React.forwardRef< 45 | React.ElementRef, 46 | React.ComponentPropsWithoutRef 47 | >(({ className, ...props }, ref) => ( 48 | 56 | )); 57 | DropdownMenuSubContent.displayName = 58 | DropdownMenuPrimitive.SubContent.displayName; 59 | 60 | const DropdownMenuContent = React.forwardRef< 61 | React.ElementRef, 62 | React.ComponentPropsWithoutRef 63 | >(({ className, sideOffset = 4, ...props }, ref) => ( 64 | 65 | 74 | 75 | )); 76 | DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; 77 | 78 | const DropdownMenuItem = React.forwardRef< 79 | React.ElementRef, 80 | React.ComponentPropsWithoutRef & { 81 | inset?: boolean; 82 | } 83 | >(({ className, inset, ...props }, ref) => ( 84 | 93 | )); 94 | DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; 95 | 96 | const DropdownMenuCheckboxItem = React.forwardRef< 97 | React.ElementRef, 98 | React.ComponentPropsWithoutRef & { 99 | checked: boolean; 100 | } 101 | >(({ className, children, checked, ...props }, ref) => ( 102 | 111 | 112 | 113 | 114 | 115 | 116 | {children} 117 | 118 | )); 119 | DropdownMenuCheckboxItem.displayName = 120 | DropdownMenuPrimitive.CheckboxItem.displayName; 121 | 122 | const DropdownMenuRadioItem = React.forwardRef< 123 | React.ElementRef, 124 | React.ComponentPropsWithoutRef 125 | >(({ className, children, ...props }, ref) => ( 126 | 134 | 135 | 136 | 137 | 138 | 139 | {children} 140 | 141 | )); 142 | DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; 143 | 144 | const DropdownMenuLabel = React.forwardRef< 145 | React.ElementRef, 146 | React.ComponentPropsWithoutRef & { 147 | inset?: boolean; 148 | } 149 | >(({ className, inset, ...props }, ref) => ( 150 | 159 | )); 160 | DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; 161 | 162 | const DropdownMenuSeparator = React.forwardRef< 163 | React.ElementRef, 164 | React.ComponentPropsWithoutRef 165 | >(({ className, ...props }, ref) => ( 166 | 171 | )); 172 | DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; 173 | 174 | const DropdownMenuShortcut = ({ 175 | className, 176 | ...props 177 | }: React.HTMLAttributes) => { 178 | return ( 179 | 183 | ); 184 | }; 185 | DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; 186 | 187 | export { 188 | DropdownMenu, 189 | DropdownMenuTrigger, 190 | DropdownMenuContent, 191 | DropdownMenuItem, 192 | DropdownMenuCheckboxItem, 193 | DropdownMenuRadioItem, 194 | DropdownMenuLabel, 195 | DropdownMenuSeparator, 196 | DropdownMenuShortcut, 197 | DropdownMenuGroup, 198 | DropdownMenuPortal, 199 | DropdownMenuSub, 200 | DropdownMenuSubContent, 201 | DropdownMenuSubTrigger, 202 | DropdownMenuRadioGroup, 203 | }; 204 | -------------------------------------------------------------------------------- /ags-validator-app/components/ui/form.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { Slot } from "@radix-ui/react-slot" 6 | import { 7 | Controller, 8 | ControllerProps, 9 | FieldPath, 10 | FieldValues, 11 | FormProvider, 12 | useFormContext, 13 | } from "react-hook-form" 14 | 15 | import { cn } from "@/lib/utils" 16 | import { Label } from "@/components/ui/label" 17 | 18 | const Form = FormProvider 19 | 20 | type FormFieldContextValue< 21 | TFieldValues extends FieldValues = FieldValues, 22 | TName extends FieldPath = FieldPath 23 | > = { 24 | name: TName 25 | } 26 | 27 | const FormFieldContext = React.createContext( 28 | {} as FormFieldContextValue 29 | ) 30 | 31 | const FormField = < 32 | TFieldValues extends FieldValues = FieldValues, 33 | TName extends FieldPath = FieldPath 34 | >({ 35 | ...props 36 | }: ControllerProps) => { 37 | return ( 38 | 39 | 40 | 41 | ) 42 | } 43 | 44 | const useFormField = () => { 45 | const fieldContext = React.useContext(FormFieldContext) 46 | const itemContext = React.useContext(FormItemContext) 47 | const { getFieldState, formState } = useFormContext() 48 | 49 | const fieldState = getFieldState(fieldContext.name, formState) 50 | 51 | if (!fieldContext) { 52 | throw new Error("useFormField should be used within ") 53 | } 54 | 55 | const { id } = itemContext 56 | 57 | return { 58 | id, 59 | name: fieldContext.name, 60 | formItemId: `${id}-form-item`, 61 | formDescriptionId: `${id}-form-item-description`, 62 | formMessageId: `${id}-form-item-message`, 63 | ...fieldState, 64 | } 65 | } 66 | 67 | type FormItemContextValue = { 68 | id: string 69 | } 70 | 71 | const FormItemContext = React.createContext( 72 | {} as FormItemContextValue 73 | ) 74 | 75 | const FormItem = React.forwardRef< 76 | HTMLDivElement, 77 | React.HTMLAttributes 78 | >(({ className, ...props }, ref) => { 79 | const id = React.useId() 80 | 81 | return ( 82 | 83 |
84 | 85 | ) 86 | }) 87 | FormItem.displayName = "FormItem" 88 | 89 | const FormLabel = React.forwardRef< 90 | React.ElementRef, 91 | React.ComponentPropsWithoutRef 92 | >(({ className, ...props }, ref) => { 93 | const { error, formItemId } = useFormField() 94 | 95 | return ( 96 |