{"id":28237016,"url":"https://github.com/bytebodger/switchboard","last_synced_at":"2025-10-14T05:21:37.953Z","repository":{"id":227359729,"uuid":"771160909","full_name":"bytebodger/switchboard","owner":"bytebodger","description":null,"archived":false,"fork":false,"pushed_at":"2024-03-12T21:55:49.000Z","size":365,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-10T15:40:51.375Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bytebodger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-03-12T19:50:53.000Z","updated_at":"2024-03-12T21:51:51.000Z","dependencies_parsed_at":"2024-03-12T22:47:47.105Z","dependency_job_id":"5d1eb5f0-2881-4b0c-98ff-57b5baea9467","html_url":"https://github.com/bytebodger/switchboard","commit_stats":null,"previous_names":["bytebodger/switchboard"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/bytebodger/switchboard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bytebodger%2Fswitchboard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bytebodger%2Fswitchboard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bytebodger%2Fswitchboard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bytebodger%2Fswitchboard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bytebodger","download_url":"https://codeload.github.com/bytebodger/switchboard/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bytebodger%2Fswitchboard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279018015,"owners_count":26086237,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-19T00:17:15.943Z","updated_at":"2025-10-14T05:21:37.938Z","avatar_url":"https://github.com/bytebodger.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Switchboard\n\nThis codebase is a React/TypeScript UI for handling email campaigns and A/B split testing via email messages.\n\n## Code Organization\nThe following are core directories:\n\n```\n\\src\n    \\common\n        \\assets\n            \\css\n            \\images\n        \\components\n        \\constants\n        \\functions\n        \\hooks\n    \\paths\n```\n\n### Paths\n\n`\\src\\paths` is a hierarchal directory that mirrors the pathing that is observable in the browser.\n\nIf the application has a path (route) that is accessible at `some.domain.com/admin`, then there should be an `Admin.tsx` component located here:\n\n```\n\\src\n    \\common\n    \\paths\n        \\admin\n            Admin.tsx\n```\n\nIf there's a path (route) that is accessible at `some.domain.com/admin/users`, there should be a `Users.tsx` component located here:\n\n```\n\\src\n    \\common\n    \\paths\n        \\admin\n            Admin.tsx\n            \\users\n                Users.tsx\n```\n\nAnd if there's a path (route) that is accessible at `some.domain.com/admin/users/create`, there should be a `Create.tsx` component located here:\n\n```\n\\src\n    \\common\n    \\paths\n        \\admin\n            Admin.tsx\n            \\users\n                Users.tsx\n                \\create\n                    Create.tsx\n```\n\n### Route Helper Files\n\nIf a route uses helper files that are _specific to that route_, then those file should be organized, by type, in the same folder where the route resides.\n\nFor example, in the following code:\n\n```\n\\src\n    \\common\n    \\paths\n        \\admin\n            Admin.tsx\n            \\components\n                AdminHeader.tsx\n            \\functions\n                isAdmin.ts\n            \\users\n                Users.tsx\n                \\hooks\n                    useUsersEndpoint.ts\n                \\create\n                    Create.tsx\n```\n\nIt's presumed that `AdminHeader.tsx` and `isAdmin.ts` are only used in the `/admin` route, or its child paths, and that `useUsersEndpoint.ts` is only used in the `/admin/users` route, or its child paths.\n\n### Common Files\n\nThe files saved under the `/src/common/` directory are those that could potentially be used amongst any of the paths.  If a file is specific to a given route, it should be saved under that route.  The `/src/common/` directory is for near-universal functionality.\n\nFor example:\n\n```\n\\src\n    \\common\n        \\assets\n            \\css\n                base-site.css\n            \\images\n                company-logo.png\n        \\components\n            CommonModal.tsx\n        \\constants\n            userStatus.ts\n        \\functions\n            getCurrentViewport.ts\n        \\hooks\n            useApiGateway.ts\n    \\paths\n        \\admin\n            Admin.tsx\n            \\components\n                AdminHeader.tsx\n            \\functions\n                isAdmin.ts\n            \\users\n                Users.tsx\n                \\hooks\n                    useUsersEndpoint.ts\n                \\create\n                    Create.tsx\n```\n\n## Tests\n\nAll components, and any significant piece of _logic_, should be accompanied by unit tests.  Those tests should live in the same directory as the logic they're testing.\n\nFor example:\n\n```\n\\src\n    \\common\n        \\assets\n            \\css\n                base-site.css\n            \\images\n                company-logo.png\n        \\components\n            CommonModal.tsx\n            CommonModal.test.tsx\n        \\constants\n            userStatus.ts\n            userStatus.test.ts\n        \\functions\n            getCurrentViewport.ts\n            getCurrentViewport.test.ts\n        \\hooks\n            useApiGateway.ts\n            useApiGateway.test.ts\n    \\paths\n        \\admin\n            Admin.tsx\n            Admin.test.tsx\n            \\components\n                AdminHeader.tsx\n                AdminHeader.test.tsx\n            \\functions\n                isAdmin.ts\n                isAdmin.test.tsx\n            \\users\n                Users.tsx\n                Users.test.tsx\n                \\hooks\n                    useUsersEndpoint.ts\n                    useUsersEndpoint.test.tsx\n                \\create\n                    Create.tsx\n                    Create.test.tsx\n```\n\nTests are configured to run before creating a test build - and to fail the build if any tests do not pass.  This should not be changed.\n\n## Linting\n\nESLint is configured to run before creating a test build - and to fail the build if the linter does not pass.  Based on team consensus, the linting rules can be altered.  But they should never be fully disabled.\n\n## Translations\n\nUnless it's explicitly known that an application will _never_ require _any_ sort of translations, great effort should be made to avoid hardcoding plain English text inside the application.  Instead, all text should be drawn from a central translations file using i18n to dynamically show the user's content.\n\nWhenever possible, translation keys should represent all-or-most of the text that's seen on screen.\n\nFor example, translated apps often use a key that looks like this:\n\n```javascript\nconst resources = {\n   en: {\n      translation: {\n         welcome_text: 'Welcome to Switchboard!',\n      },\n   },\n}\n```\n\nThis is problematic because the key `welcome_text` is generic and there could be a need for \"welcome text\" in many places throughout the app.  Instead, the translation key should look like this:\n\n```javascript\nconst resources = {\n   en: {\n      translation: {\n         'Welcome to Switchboard!': 'Welcome to Switchboard!',\n      },\n   },\n}\n```\n\nThis also makes the application easier to troubleshoot.  When a new developer sees \"Welcome to Switchboard!\" on the screen, they can easily find the code that's driving that display.  Because they only need to search the code for that literal string.\n\n## Toolset\n\nThe following are not required for every project, but are preferred tools/approaches:\n\n* **Language:** TypeScript\n* **Library:** React, with a heavy focus on Hooks, functional components, and the Context API\n* **Design:** Material UI (which would be switched out for Ionic if the site must be compiled into native apps)\n* **State Management:** Ideally, this is handled with no outside libaries - but when one is needed, Zustand is preferred due its simplicity and lack of boilerplate\n* **Pathing:** React Router\n* **Asynchronous Calls:** React Query\n* **Translations:** i18n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbytebodger%2Fswitchboard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbytebodger%2Fswitchboard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbytebodger%2Fswitchboard/lists"}