{"id":16488786,"url":"https://github.com/austindd/simspace-challenge","last_synced_at":"2026-06-11T17:31:27.445Z","repository":{"id":173461444,"uuid":"353821543","full_name":"austindd/simspace-challenge","owner":"austindd","description":null,"archived":false,"fork":false,"pushed_at":"2023-06-07T20:02:38.000Z","size":494,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-01T02:25:46.111Z","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/austindd.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,"publiccode":null,"codemeta":null}},"created_at":"2021-04-01T20:40:49.000Z","updated_at":"2023-06-07T20:04:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"7e9b1170-51d6-422e-b4bc-d9daf5ed0dbc","html_url":"https://github.com/austindd/simspace-challenge","commit_stats":null,"previous_names":["austindd/simspace-challenge"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/austindd/simspace-challenge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/austindd%2Fsimspace-challenge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/austindd%2Fsimspace-challenge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/austindd%2Fsimspace-challenge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/austindd%2Fsimspace-challenge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/austindd","download_url":"https://codeload.github.com/austindd/simspace-challenge/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/austindd%2Fsimspace-challenge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34211061,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"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":"2024-10-11T13:39:56.238Z","updated_at":"2026-06-11T17:31:27.430Z","avatar_url":"https://github.com/austindd.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SimSpace Code Challenge\n\n## Development Quick-Start\n\nStart by cloning the repo and running `yarn` to download the dependencies.\n\nThere are 2 ways to start up the dev environment:\n  1. In one terminal window, run `yarn dev:server` to start the API proxy server, which I added for personal practice. In a second terminal window, run `yarn dev:client` to start the usual CRA dev server, which should open the app in your browser.\n  2. Run `yarn dev`. This executes the `dev:server` and `dev:client` scripts in parallel in a single terminal window. In some cases, the client app will load before the server is fully started, so the API data won't load properly. If this happens, just refresh the page after the server starts.\n\n## Architecture\n\nFor the data fetching, I put together a local proxy server (located at `./server/api-proxy.ts`) that fetches the data and passes it through to the client. This could be updated to transform the data or store it in a database, but this was mostly just for personal practice.\n\nAs for the front-end, the architecture is just a set of relatively independent components, which use a sort of state-reducer pattern for state management. This \"reducer\" is really a React context provider, which handles actions from consumers, and propagates the state to each component that needs it.\n\nAs for CSS styles, I really like libraries like Bootstrap and Tailwind, but I decided to keep it simple and go with plain CSS. The class names could be cleaned up a little bit, but I tried to use the BEM convention, so it's relatively clear in my opinion.\n\n### State Management\n\nThe state-management could be accomplished in a simpler way with a true reducer pattern, like `useReducer` or `redux`. However, I started with a context provider, and rather than rebuilding it from the ground up, I ended up evolving it into a makeshift state-reducer. If I had more time, I would probably refactor this, but it works well for now.\n\n### Data Models\n\nAs for the data models themselves, I defined the core data types and helper functions (decoding/encoding, remote fetching, etc) in `./src/data/api-data.ts`. I don't use any classes, preferring to be as functional as possible. Instead, everything is organized into namespaces/modules.\n\nIt's also worth noting that I'm using `fp-ts` with `io-ts` decoding the data from the API. This turns out to be a very nice sanity check for me, since I know I can trust the data that passes through the decoders. It also allows me to isolate errors due to incorrect assumptions about the API data.\n\n### Programming Style\n\nI strongly prefer functional programming, so almost everything is done in a functional style. There are a few bits of code written in imperative style where it makes sense, but it's rare.\n\nI make heavy use of `fp-ts`, partially because I know that's what SimSpace tends to use, but also because I really love the library. I mostly used the `Option`, `Either` and `TaskEither` modules, but a case could be made for other uses. I tend to shy away from category-theoretical stuff, especially when making a first pass at a problem. I am fine using abstractions like monads and monoids, but you won't find them here.\n\nOne nice result of using the `Option` type in my state models was that I can easily differentiate an *uninitialized* state from *invalid* or *empty* states. If it's `None`, then I know it's simply not set.\n\n### Bonus Features\n\nThere are a couple of additional features that were not specified:\n  1. I added a *'fuzzy match'* feature that attempts to search for dog breeds by looking at similarities between the search term and the breed names. This is based on the classic *Levenshtein-distance* algorithm. I tried to make it a bit smarter by moving *exact* substring matches to the front of the search results, and using the Levenshtein-distance rankings for the rest.\n  2. I added a toggle switch to switch between the normal search (exact substring match) and the fuzzy search, so you can play around with both.\n  3. The loading icon is really nice-looking and animated. I used a library called `react-loading-spinners` for that.\n\n## Final Thoughts\n\nI haven't had a ton of experience implementing search features, but this one was fun and interesting, especially with all the dynamic element changes on the screen. It pulled together a lot of concepts, so I think it was a suitable challenge. It was also really nice to play around with `fp-ts`, since I haven't been able to use it a whole lot for work purposes. If it turns out that I'm a good fit for SimSpace, then I definitely look forward to perks like this.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faustindd%2Fsimspace-challenge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faustindd%2Fsimspace-challenge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faustindd%2Fsimspace-challenge/lists"}