<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Portfolio &#8211; hackanooga</title>
	<atom:link href="/tag/portfolio/feed/" rel="self" type="application/rss+xml" />
	<link>/</link>
	<description>Confessions of a homelab hacker</description>
	<lastBuildDate>Tue, 12 Mar 2024 21:00:42 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.2</generator>

<image>
	<url>/wp-content/uploads/2024/03/cropped-cropped-avatar-32x32.png</url>
	<title>Portfolio &#8211; hackanooga</title>
	<link>/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Hoots Wings</title>
		<link>/hoots-wings/</link>
		
		<dc:creator><![CDATA[mikeconrad]]></dc:creator>
		<pubDate>Wed, 12 Jul 2023 15:32:44 +0000</pubDate>
				<category><![CDATA[Software Engineering]]></category>
		<category><![CDATA[Portfolio]]></category>
		<guid isPermaLink="false">/?p=495</guid>

					<description><![CDATA[While working for Morrison I had the pleasure of building a website for Hoots Wings. The CMS was Perch and it was mostly HTML, CSS, PHP and JavaScript on the frontend, however I built out a customer store locator using NodeJS and VueJS. I was the sole frontend developer responsible for taking the designs from [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>While working for <a href="https://morrison.agency/">Morrison</a> I had the pleasure of building a website for <a href="https://hootswings.com/">Hoots Wings.</a>  The CMS was <a href="https://grabaperch.com/">Perch</a> and it was mostly HTML, CSS, PHP and JavaScript on the frontend, however I built out a customer store locator using NodeJS and VueJS.</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="1024" height="514" src="/wp-content/uploads/2024/03/hoots-home.webp" alt="" class="wp-image-498" srcset="/wp-content/uploads/2024/03/hoots-home.webp 1024w, /wp-content/uploads/2024/03/hoots-home-300x151.webp 300w, /wp-content/uploads/2024/03/hoots-home-768x386.webp 768w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>I was the sole frontend developer responsible for taking the designs from SketchUp and translating them to the site you see now.  Most of the blocks and templates are built using a mix of PHP and HTML/SCSS.  There was also some JavaScript for things like getting the users location and rendering popups/modals.</p>



<p>The store locator was a separate piece that was built in Vue2.0  with a NodeJS backend.  For the backend I used <a href="https://keystonejs.com/">KeystoneJS</a> to hold all of the store information.  There was also some custom development that was done in order to sync the stores added via the CMS with <a href="https://www.yext.com/">Yext</a> and vice versa.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="522" src="/wp-content/uploads/2024/03/hoots-locations-min-1024x522.webp" alt="hootswings.com screenshot" class="wp-image-502" srcset="/wp-content/uploads/2024/03/hoots-locations-min-1024x522.webp 1024w, /wp-content/uploads/2024/03/hoots-locations-min-300x153.webp 300w, /wp-content/uploads/2024/03/hoots-locations-min-768x391.webp 768w, /wp-content/uploads/2024/03/hoots-locations-min-1536x783.webp 1536w, /wp-content/uploads/2024/03/hoots-locations-min.webp 1900w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>For that piece I ended up having to write a custom integration in Perch that would connect to the NodeJS backend and pull the stores but also make sure that those were in sync with Yext.  This required diving into the Yext API some and examining a similar integration that we had for another client site.</p>



<p>Unfortunately I don&#8217;t have any screen grabs of the admin side of things since that is proprietary but the system I built allowed a site admin to go in and add/edit store locations that would show up on the site and also show up in Yext with the appropriate information.</p>



<h4 class="wp-block-heading">Screenshots</h4>



<p>Here are some full screenshots of the site.<br></p>



<p>Homepage</p>



<figure class="wp-block-image size-large"><img decoding="async" width="635" height="1024" src="/wp-content/uploads/2024/03/hootswings-home-full-635x1024.webp" alt="hootswings.com homepage screenshot" class="wp-image-504" srcset="/wp-content/uploads/2024/03/hootswings-home-full-635x1024.webp 635w, /wp-content/uploads/2024/03/hootswings-home-full-186x300.webp 186w, /wp-content/uploads/2024/03/hootswings-home-full-768x1238.webp 768w, /wp-content/uploads/2024/03/hootswings-home-full-953x1536.webp 953w, /wp-content/uploads/2024/03/hootswings-home-full-1270x2048.webp 1270w, /wp-content/uploads/2024/03/hootswings-home-full-scaled.webp 1588w" sizes="(max-width: 635px) 100vw, 635px" /></figure>



<p></p>



<p>Menu Page</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="550" height="1024" src="/wp-content/uploads/2024/03/hootswings-menu-page-550x1024.png" alt="" class="wp-image-505" srcset="/wp-content/uploads/2024/03/hootswings-menu-page-550x1024.png 550w, /wp-content/uploads/2024/03/hootswings-menu-page-161x300.png 161w, /wp-content/uploads/2024/03/hootswings-menu-page-768x1430.png 768w, /wp-content/uploads/2024/03/hootswings-menu-page-825x1536.png 825w, /wp-content/uploads/2024/03/hootswings-menu-page-1100x2048.png 1100w, /wp-content/uploads/2024/03/hootswings-menu-page.png 1920w" sizes="(max-width: 550px) 100vw, 550px" /></figure>



<p></p>



<p>Locations Page</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="432" height="1024" src="/wp-content/uploads/2024/03/hootswings-locations-page-432x1024.png" alt="" class="wp-image-506" srcset="/wp-content/uploads/2024/03/hootswings-locations-page-432x1024.png 432w, /wp-content/uploads/2024/03/hootswings-locations-page-126x300.png 126w, /wp-content/uploads/2024/03/hootswings-locations-page-768x1822.png 768w, /wp-content/uploads/2024/03/hootswings-locations-page-863x2048.png 863w, /wp-content/uploads/2024/03/hootswings-locations-page.png 1920w" sizes="(max-width: 432px) 100vw, 432px" /></figure>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Hilger Grading Portal</title>
		<link>/hilger-grading-portal/</link>
		
		<dc:creator><![CDATA[mikeconrad]]></dc:creator>
		<pubDate>Sun, 21 May 2023 20:07:50 +0000</pubDate>
				<category><![CDATA[Software Engineering]]></category>
		<category><![CDATA[Portfolio]]></category>
		<guid isPermaLink="false">/?p=509</guid>

					<description><![CDATA[Back around 2014 I took on my first freelance development project for a Homeschool Co-op here in Chattanooga called Hilger Higher Learning. The problem that they were trying to solve involved managing grades and report cards for their students. In the past, they had a developer build a rudimentary web application that would allow them [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Back around 2014 I took on my first freelance development project for a Homeschool Co-op here in Chattanooga called <a href="https://www.hhlearning.com/" data-type="link" data-id="https://www.hhlearning.com/">Hilger Higher Learning</a>.  The problem that they were trying to solve involved managing grades and report cards for their students.  In the past, they had a developer build a rudimentary web application that would allow them to enter grades for students, however it lacked any sort of concurrency meaning that if two teachers were making changes to the same student at the same time, teacher b&#8217;s changes would overwrite teacher a&#8217;s changes.  This was obviously a huge headache.</p>



<p>I built out the first version of the app using PHP and HTML, CSS and Datatables with lots of jQuery sprinkled in.  I built in custom functionality that allowed them to easily compile and print all the report cards for all students with the simple click of a button.  It was a game changer or them and streamlined the process significantly.</p>



<p>That system was in production for 5 years or so with minimal updates and maintance.  I recently rebuilt it using React and ChakraUI on the frontend and <a href="https://keystonejs.com/">KeystoneJS</a> on the backend.  I also modernized the deployment by building Docker images for the frontend/backend.  I actually ended up keeping parts of it in PHP due to the fact that I couldn&#8217;t find a JavaScript library that would solve the challenges I had.  Here are some screenshots of it in action:</p>



<p></p>



<p>This is the page listing all teachers in the system and whether or not they have admin privileges.  Any admin user can grant other admin users this privilege.  There is also a button to send the teacher a password reset email (via Postmark API integration) and an option that allows admin users to impersonate other users for troubleshooting and diagnostic purposes.</p>



<p></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="514" src="/wp-content/uploads/2024/03/hilger-portal-all-teachers-1024x514.webp" alt="" class="wp-image-511" srcset="/wp-content/uploads/2024/03/hilger-portal-all-teachers-1024x514.webp 1024w, /wp-content/uploads/2024/03/hilger-portal-all-teachers-300x150.webp 300w, /wp-content/uploads/2024/03/hilger-portal-all-teachers-768x385.webp 768w, /wp-content/uploads/2024/03/hilger-portal-all-teachers-1536x770.webp 1536w, /wp-content/uploads/2024/03/hilger-portal-all-teachers.webp 1920w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>The data is all coming from the KeystoneJS backend GraphQL API.  I am using <a href="https://www.npmjs.com/package/urql">urql</a> for fetching the data and handling mutations.  This is the page that displays students.  It is filterable and searchable.  Teachers also have the ability to mark a student as active or inactive for the semester as well as delete them from the system.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="513" src="/wp-content/uploads/2024/03/hilger-portal-all-students-1-1024x513.webp" alt="" class="wp-image-525" srcset="/wp-content/uploads/2024/03/hilger-portal-all-students-1-1024x513.webp 1024w, /wp-content/uploads/2024/03/hilger-portal-all-students-1-300x150.webp 300w, /wp-content/uploads/2024/03/hilger-portal-all-students-1-768x385.webp 768w, /wp-content/uploads/2024/03/hilger-portal-all-students-1-1536x770.webp 1536w, /wp-content/uploads/2024/03/hilger-portal-all-students-1.webp 1920w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>Clicking on a student takes the teacher/admin to an edit course screen where they can add and remove courses for each student.  A teacher can add as many courses as they need.  If multiple teachers have added courses for this student, the user will only see the courses they have entered.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="514" src="/wp-content/uploads/2024/03/hilger-portal-edit-student-1024x514.webp" alt="" class="wp-image-526" srcset="/wp-content/uploads/2024/03/hilger-portal-edit-student-1024x514.webp 1024w, /wp-content/uploads/2024/03/hilger-portal-edit-student-300x150.webp 300w, /wp-content/uploads/2024/03/hilger-portal-edit-student-768x385.webp 768w, /wp-content/uploads/2024/03/hilger-portal-edit-student-1536x770.webp 1536w, /wp-content/uploads/2024/03/hilger-portal-edit-student.webp 1920w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>There is another page that allows admin users to view and manage all of the parents in the system.  It allows them to easily send a password reset email to the parents as well as to view the parent portal.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="513" src="/wp-content/uploads/2024/03/hilger-portal-parents-1024x513.webp" alt="" class="wp-image-527" srcset="/wp-content/uploads/2024/03/hilger-portal-parents-1024x513.webp 1024w, /wp-content/uploads/2024/03/hilger-portal-parents-300x150.webp 300w, /wp-content/uploads/2024/03/hilger-portal-parents-768x385.webp 768w, /wp-content/uploads/2024/03/hilger-portal-parents-1536x770.webp 1536w, /wp-content/uploads/2024/03/hilger-portal-parents.webp 1920w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Technologies used</h2>



<ul class="wp-block-list">
<li>Digital Ocean Droplet (Server) &#8211; Ubuntu Server</li>



<li>Docker (Frontend, Backend, PHP, Postgresql database)</li>



<li>Git</li>



<li>NodeJS</li>



<li>PHP</li>



<li>ChakraUI</li>



<li>KeystoneJS</li>



<li>Postmark</li>



<li>GraphQL</li>



<li>Typescript</li>



<li>React</li>



<li>urql</li>
</ul>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
