- Welcome to Work-In!
- Technologies
- Relevant Documents
- Navigating and using Work-In
- Running Work-In on your local machine
- Technical Challenges and Implementation Details
- Future Features and Plans
Work-In is a web application inspired by the online travel booking site Airbnb. This is a clone that aims to use the model of the online reservation system for workspaces and short-term/one-time rental of establishments such as offices, rooms, and clinics.
You can try this app at the following link: https://work-in-live.herokuapp.com/
- Express.js
- Sequelize
- PostgreSQL
- React.js
- Redux
- Node.js
- AWS S3
- Pure CSS (no libraries or extensions)
When you navigate to the homepage of the application you will be presented with this splash screen:

From here you can click on the login or signup links to bring up a modal where you can login if you have an account, signup if you don't, or otherwise just login with the Demo Host account.
You can also begin searching immediately for a location or a listing by name, and suggested results will begin to appear for you.
Clicking on one of those suggestions will direct you to the page for that listing:
Clicking on the "Listings" navigation link will take you to a page that shows all of the available listings on the platform.
Alternatively, you can search for more specific listings in the search bar:
Pressing enter during your search input or pressing the search icon will direct you to a page with listings that match only your search criterion:
Clicking on the card for any of the listings on the listing search page will navigate to the specific page for that individual listing.
From here you can click on the "Show All Photos" button to bring up an image viewer where you can look at images in better proportions and more detail. You can click the left or right arrows to navigate between the various images available for the listing.
If you decide that you like the listing and would like to reserve it for some time, you can check availability and set your dates with the date pickers in the booking box.
You will be presented with the details of the booking you have selected, the total for that reservation, and the math used to calculate that total. If you wish to proceed with booking, you can click the "Reserve This Listing" button. This will bring up a confirmation popup modal:
If you then click on the "Reserve Listing" button, your booking will be created! You have now reserved a workspace!
Before running Work-In in the development environment you'll need an up to date installation of Node.js and PostgreSQL. These instructions will assume that you already have both of those already installed and configured. Also assure ahead of time that no other processes are running on port 5000.
From wherever you wish to place your development installation:
git clone https://github.com/KagenLH/Work-In.gitOnce the repository is pulled from GitHub, you're ready to start setup. First install dependencies:
cd Work-In
npm installCreate a .env file in the root of the backend folder. Copy the values from the .env.example file. You can either copy all of the values verbatim or change the values to values of your liking. Keep in mind that you won't be able to add new images/listings with your local application because it requires the secret AWS access key. You can change the JWT_SECRET if you like. Your .env should look like this:
PORT=5000
DB_USERNAME=workin_app
DB_DATABASE=workin_development
DB_PASSWORD=password
DB_HOST=localhost
JWT_SECRET=secret
JWT_EXPIRES_IN=608000
Now you can create, migrate, and seed the database
npm run sequelize db:create
npm run sequelize db:migrate
npm run sequelize db:seed:allNow you're ready to start the API server:
cd backend
npm startOpen another terminal window, and from the root project directory navigate to the front-end and start the React server:
cd frontend
npm startWhen creating the image viewing overlay there was no pre-existing template available for me to work from, so I approached this task with a mind for designing my own from the ground up. This presented a number of challenges.
The image viewer should appear on the page and cover every other element on the page. When it is on it should be the only feature on the page. I decided to approach this by creating a new component ImageViewer to hold the image viewer in that would be of fixed position, cover the entire page, and obscure everything behind it:
.image-viewer__container {
position: fixed;
background: rgba(0, 0, 0, 0.9);
width: 100%;
height: 100%;
z-index: 5;
}with a proper overlay in place, now the image viewer applet could be added over top of it.
I decided that the image viewer should only display one image at a time with the ability to switch between them with side arrow buttons. In order to manage the image that should be shown at any given time, a new imageViewer piece of global state was created and configured in the Redux store. The ImageViewer component would read that slice of state to determine the currentImage, and display that image accordingly:
<div
className="sliding-image__container"
>
<ImageViewerImage image={currentImage}/>
</div>The images for the current listing page would be passed as a prop to the ImageViewer component, and anytime that one of the side arrow buttons is clicked, the imageViewer.currentImage slice of state is updated to the value of the next index in the images array.
Users editing their listings should be able to add new images to their listing, or remove existing ones. This should be facilitated in a pleasant, graphical, and user friendly manner. This required a significantly more sophisticated approach than editing the existing text fields for the listing.
Images that are already attached to the listing already have rows in the database. They are displayed and added into the form by their access URL on AWS, and thus sending in their raw data is not possible using the same methods that the Listing Post uses without doing extra work. Furthermore, there is simply no need to send these images in again with the edit request since they already exist. Therefore separate containers and management are needed for images the user has already created and images that the user is currently adding. I decided to implement this by separating the two form fields into two separate slices of local component state:
const [images, setImages] = useState([]);
const [newImages, setNewImages] = useState([]);Each is rendered in a separate portion of the DOM using their respective piece of state. When the time comes to submit the form, only the newImages array is appended into the FormData object to be sent to the server.
When the user clicks on one of the images they have in the form, it will be removed. If its an image they have just added, it will simply be removed from the form. If its an image that already existed on their listing, it will be destroyed in the database as well. Because each image has a row in the table and in the database the listing owns the image via a foreign key on the image table, simply deleting the image in the database is sufficient to detach it from the listing.
First, send the appropriate request to the API server:
const res = await csrfFetch(`/api/images/${e.target.getAttribute("urlkey")}`, {
method: 'DELETE',
});Where urlkey is a custom attribute given to each image indicating the unique ID for that image.
Next, handle the request and destroy the image on the backend:
const image = await Image.findByPk(imageId);
const listing = await Listing.findByPk(image.listingId);
if(userId === listing.userId) {
await image.destroy();
res.json({ message: "Image deletion was successful." });
} else {
const err = new Error("You can only delete images that you own.");
next(err);
}-
User Profiles and uploaded avatars
-
User Inbox and Messages
-
Ability to share listings with other platforms such as messaging, social media, etc.
-
Google Geolocation and Google Maps API integration









