• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
jjwon

jjwon

ffxiv

Public
Like
ffxiv
Home
Code
6
.claude
1
.vtignore
CLAUDE.md
TODO.md
deno.json
H
server.ts
Branches
1
Pull requests
Remixes
History
Environment variables
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
TODO.md
Code
/
TODO.md
Search
7/20/2025
Viewing readonly version of main branch: v82
View latest version
TODO.md

Phase 1: The Data Layer (Val Town) Goal: Create a single, reliable Val Town API endpoint that provides all the necessary instance data, properly formatted and cached.

Task 1.1: Research the XIVAPI /instancecontent endpoint.

Familiarize yourself with its structure. You will need to request specific columns to get the data required by the PRD.

Identify the fields for:

Name: The instance's name.

ContentType.Name: This will give you the "Type" (e.g., "Dungeon").

ContentFinderCondition.Name: This often contains the common name for the duty.

Unlock Quest: This can be tricky. Look for a field like QuestUnlock which might be an object containing the quest name. You may need to experiment to find the most reliable field.

Note on Pagination: XIVAPI results are paginated (e.g., 100 results per page). Your script must loop through all pages to collect every instance.

Task 1.2: Create the Val Town "Val" (Function).

Create a new val in your Val Town workspace (e.g., ffxivInstanceList).

Write JavaScript code inside this val to perform the following logic:

Initialize an empty array, e.g., allInstances = [].

Make an initial fetch call to https://xivapi.com/instancecontent?columns=ID,Name,ContentType.Name,....

Write a while or for loop that continues as long as the API response indicates there is a next page (Pagination.PageNext != null). In each loop, fetch the next page and add the results to your allInstances array.

Once all instances are collected, process the array: Group the instances by their expansion. You may need another API call to /expansion or hardcode the expansion order and level caps to achieve this grouping.

Return the final, grouped JSON object.

Task 1.3: Add Caching to the Val.

Instance data changes very infrequently (only with major patches). To avoid hitting XIVAPI rate limits and to make your app load instantly, add caching.

A simple approach in Val Town is to store the result and a timestamp.

Modify your val: before fetching, check if a cached result exists and if it's recent (e.g., less than 24 hours old). If it is, return the cached data immediately. If not, perform the fetch, then update the cache with the new data and timestamp.

At the end of this phase, you will have a URL that returns a clean JSON object of all game instances, grouped by expansion.

Phase 2: The Frontend Structure (HTML & CSS) Goal: Create the static skeleton of your web application.

Task 2.1: Create index.html.

Set up a basic HTML5 document.

In the , add a (e.g., "FFXIV Completion Tracker"), and link to your style.css and script.js files.

In the <body>, create the main layout:

A

containing an

for the title and an .

A

element. This empty div is where your JavaScript will render the list.

Task 2.2: Create style.css.

Write some basic CSS to make the app usable from the start.

Style the body for a nice background color and font.

Center the main content and give it a max-width.

Style the header and search bar.

Create CSS classes for the elements you will generate with JavaScript:

.expansion-group { ... } (for the container of an expansion's instances)

.expansion-header { ... } (for the "Heavensward" title)

.instance-row { display: grid; grid-template-columns: 3fr 1fr 1fr 1fr; ... } (to align the name and checkboxes nicely)

.checkbox-label { ... }

At the end of this phase, you will have a static webpage with a title, a search bar, and an empty area, all styled and ready for data.

Phase 3: Interactive Logic (JavaScript) Goal: Use JavaScript to connect the data to the UI and implement all user-facing features.

Task 3.1: Fetch and Render Data.

In script.js, write an async function to fetch data from your Val Town URL.

Once you have the JSON data, write a render(data) function.

This function will:

Get the

element.

Clear any existing content inside it.

Loop through your grouped data (e.g., for each expansion).

For each expansion, create and append an

header.

Then, loop through the instances within that expansion. For each instance, dynamically create an .instance-row div containing the Name, Type, Quest, and the two checkboxes with labels. Crucially, give each checkbox a unique id or data-id attribute based on the instance ID from the API (e.g., data-instance-id="${instance.ID}").

Task 3.2: Implement State Persistence.

Saving: Create a saveState() function. This function will find all checkboxes on the page, loop through them, and build a JSON object like { "instance_id_123": { unlocked: true, completed: false }, ... }. It will then save this object to localStorage.setItem('ffxivCompletionState', JSON.stringify(stateObject)).

Loading: Create a loadState() function. This function will get the item from localStorage, parse it, and then loop through the rendered checkboxes on the page, setting their checked property to true or false based on the loaded data.

Binding: Call loadState() after you first render the list. Add an event listener to the main instance-container that listens for change events on the checkboxes. When an event occurs, call saveState().

Task 3.3: Implement Search/Filter.

Add an input event listener to the #search-bar.

Inside the listener function, get the search bar's current value (and convert to lowercase).

Get all the .instance-row elements.

Loop through them. For each row, check if its text content (in lowercase) includes the search term.

If it matches, set row.style.display = ''. If not, set row.style.display = 'none'.

At the end of this phase, your application will be fully functional on Val town.

// July 20 2025

Update all titles so that they follow APA titlecase style.

Make all text within each row to adopt a body style and unbolded styling.

Center align all columns.

Set a max width of 1200px for the page content except the header.

It bothers me that the instance name column and the header text above it are not aligned. Can we move the bulk edit checkboxes to show outside of the table on hover?

The unlocked and completed checkboxes are still not center aligned vertically.

Let's make the background of the table white so that it stands out from the page background.

Can you also make the content within the header have a max of 1200px? And let's remove any padding around the header so that it's full bleed.

FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.