├── .gitignore
├── README.md
├── public
├── img
│ ├── black_shorthair.png
│ ├── orange_longhair.png
│ └── tabby_fireplace.png
├── css
│ └── styles.css
└── js
│ └── script.js
├── views
├── partials
│ └── header.ejs
├── item.ejs
└── index.ejs
├── models
└── Item.js
├── package.json
└── server.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | node_modules
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fullstack_Template_EJS_MongoDB
2 |
--------------------------------------------------------------------------------
/public/img/black_shorthair.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mayanwolfe/Fullstack_Template_EJS_MongoDB/HEAD/public/img/black_shorthair.png
--------------------------------------------------------------------------------
/public/img/orange_longhair.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mayanwolfe/Fullstack_Template_EJS_MongoDB/HEAD/public/img/orange_longhair.png
--------------------------------------------------------------------------------
/public/img/tabby_fireplace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mayanwolfe/Fullstack_Template_EJS_MongoDB/HEAD/public/img/tabby_fireplace.png
--------------------------------------------------------------------------------
/views/partials/header.ejs:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/models/Item.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const ItemSchema = new mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: true
7 | },
8 | description: {
9 | type: String,
10 | required: true
11 | }
12 | });
13 |
14 | module.exports = mongoose.model('Item', ItemSchema);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fullstack_template_ejs_mongodb",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "cors": "^2.8.5",
14 | "dotenv": "^16.3.1",
15 | "ejs": "^3.1.9",
16 | "express": "^4.18.2",
17 | "mongoose": "^7.6.2"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/views/item.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Item
5 |
6 |
7 |
8 | <%- include('./partials/header') %>
9 | Item Page
10 |
11 |
12 |
17 |
18 |
19 |
25 |
26 |
27 |
28 | <% items.forEach((item) => { %>
29 |
30 | <%= item.name %>: <%= item.description %>
31 | Edit
32 | Delete
33 |
34 | <% }); %>
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/public/css/styles.css:
--------------------------------------------------------------------------------
1 | /* Navbar styling */
2 | .navbar {
3 | background-color: #333;
4 | overflow: hidden;
5 | }
6 |
7 | /* Navbar list */
8 | .nav-list {
9 | list-style-type: none;
10 | margin: 0;
11 | padding: 0;
12 | }
13 |
14 | /* Navbar items */
15 | .nav-item {
16 | float: left;
17 | }
18 |
19 | /* Navbar links */
20 | .nav-link {
21 | display: block;
22 | color: white;
23 | text-align: center;
24 | padding: 14px 20px;
25 | text-decoration: none;
26 | }
27 |
28 | /* Change color on hover */
29 | .nav-link:hover {
30 | background-color: #555;
31 | color: white;
32 | }
33 |
34 |
35 | body {
36 | font-family: Arial, sans-serif;
37 | line-height: 1.6;
38 | margin: 20px;
39 | padding: 0;
40 | }
41 |
42 | header {
43 | background-color: #f9f9f9;
44 | text-align: center;
45 | padding: 20px;
46 | }
47 |
48 | .sub-header {
49 | font-size: 1.2em;
50 | }
51 |
52 | h2 {
53 | background-color: #f0f0f0;
54 | padding: 10px;
55 | }
56 |
57 | ul, ol {
58 | margin-left: 20px;
59 | }
60 |
61 | blockquote {
62 | background-color: #f0f0f0;
63 | padding: 10px;
64 | margin: 20px 0;
65 | }
66 |
67 | footer {
68 | text-align: center;
69 | margin-top: 30px;
70 | }
71 |
72 | /* Carousel styling */
73 | #carousel {
74 | width: 600px;
75 | height: 400px;
76 | overflow: hidden;
77 | margin: auto;
78 | }
79 |
80 | #carousel img {
81 | width: 100%;
82 | height: 100%; /* Changed from auto */
83 | object-fit: contain; /* Make the image fit within the dimensions */
84 | display: none; /* Hide images by default */
85 | }
86 |
87 | #carousel img.active {
88 | display: block; /* Show active image */
89 | }
90 |
--------------------------------------------------------------------------------
/public/js/script.js:
--------------------------------------------------------------------------------
1 | function startCarousel() {
2 | let activeImage = 0;
3 | const images = document.querySelectorAll("#carousel img");
4 |
5 | function cycleImages() {
6 | if (!images[activeImage]) {
7 | // If the active image is undefined, stop the interval and return
8 | clearInterval(intervalId);
9 | return;
10 | }
11 | //Loops the carousel
12 | images[activeImage].classList.remove("active");
13 | activeImage = (activeImage + 1) % images.length;
14 | images[activeImage].classList.add("active");
15 | }
16 |
17 | let intervalId = setInterval(cycleImages, 3000);
18 | }
19 |
20 | // Function to check for error in query parameters and show an alert
21 | function checkForError() {
22 | const urlParams = new URLSearchParams(window.location.search);
23 | if (urlParams.has('error')) {
24 | alert("Validation failed. Name and description are required.");
25 | }
26 | }
27 |
28 | // Start the carousel and check for errors when the page loads
29 | window.onload = function() {
30 | startCarousel();
31 | checkForError();
32 | };
33 |
34 | function editItem(id, name, description) {
35 | // Populate the hidden field with the id
36 | document.getElementById("updateId").value = id;
37 |
38 | // Populate the form fields with the existing item's data
39 | document.getElementById("updateName").value = name;
40 | document.getElementById("updateDescription").value = description;
41 |
42 | // Update the form's action attribute
43 | document.getElementById("updateForm").action = `/item/update/${id}`;
44 | }
45 |
46 | async function deleteItem(id) {
47 | try {
48 | const response = await fetch(`http://localhost:3000/item/delete/${id}`, {
49 | method: 'DELETE',
50 | });
51 | if (response.ok) {
52 | console.log('Item deleted successfully');
53 | location.reload();
54 | } else {
55 | console.log('Failed to delete item');
56 | }
57 | } catch (error) {
58 | console.log('An error occurred:', error);
59 | }
60 | }
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | const express = require('express');
3 | const mongoose = require('mongoose');
4 | const cors = require('cors');
5 | const Item = require('./models/Item')
6 |
7 | const app = express();
8 |
9 | const port = process.env.PORT || 3000;
10 |
11 | // Connect to MongoDB - create a .env file and populate these environment variables with your database information.
12 | mongoose.connect(`mongodb+srv://${process.env.MONGODB_USERNAME}:${process.env.MONGODB_PASSWORD}@cluster0.pztfz.mongodb.net/${process.env.MONGODB_COLLECTION_NAME}?retryWrites=true&w=majority`)
13 | .then(() => console.log('MongoDB connected'))
14 | .catch(err => console.log(err));
15 |
16 | // Enable CORS
17 | app.use(cors());
18 |
19 | // Serve static files
20 | app.use(express.static('public'));
21 |
22 | // Parse requests for strings/arrays, use extended: true for nested requests
23 | app.use(express.urlencoded({ extended: false }));
24 |
25 | //Parse JSON requests
26 | app.use(express.json())
27 |
28 | // Set EJS as templating engine
29 | app.set('view engine', 'ejs');
30 |
31 | // Routes
32 | app.get('/', (req, res) => {
33 | res.render('index')
34 | });
35 |
36 |
37 | app.get('/item', async (req, res) => {
38 | const items = await Item.find({});
39 | res.render('item', { items });
40 | });
41 |
42 | // Create
43 | app.post('/item', async (req, res) => {
44 | const newItem = new Item(req.body);
45 | try {
46 | await newItem.save();
47 | res.redirect('/item');
48 | } catch (err) {
49 | //you can set up custom error handling on the client side using this information
50 | res.redirect('/item?error=true');
51 | }
52 | });
53 |
54 | // Update
55 | app.post('/item/update/:id', async (req, res) => {
56 | const { id } = req.params;
57 | const { name, description } = req.body;
58 | try {
59 | await Item.findByIdAndUpdate(id, { name, description });
60 | res.redirect('/item');
61 | } catch (err) {
62 | //you can set up custom error handling on the client side using this information
63 | res.redirect(`/item?error=true`);
64 | }
65 | });
66 |
67 | // Delete
68 | app.delete('/item/delete/:id', async (req, res) => {
69 | const { id } = req.params;
70 | await Item.findByIdAndDelete(id);
71 | //Send success back to the client-side function
72 | res.status(200).json({ message: 'Item deleted successfully' });
73 | });
74 |
75 |
76 | // Start the server
77 | app.listen(port, () => {
78 | console.log(`Server running on http://localhost:${port}`);
79 | });
80 |
--------------------------------------------------------------------------------
/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Home
5 |
6 |
7 |
8 |
9 | <%- include('./partials/header') %>
10 | Welcome to KittenLand!
11 |
12 |
13 |
14 |
15 |
21 | All images generated by DALL-E 3
22 |
23 | Why Kittens?
24 | Kittens are more than just adorable balls of fluff; they are tiny, playful, and inquisitive companions that bring joy and happiness to any household. Whether you're a first-time kitten owner or a seasoned feline enthusiast, you've come to the right place!
25 |
26 |
27 |
28 | What We Offer
29 |
30 | Kitten Care Guides: Learn how to take care of your new feline friend from day one!
31 | Adoption Tips: Get expert advice on choosing the perfect kitten for your lifestyle.
32 | Product Reviews: Discover the best toys, accessories, and food for your little furball.
33 | Photo Galleries: Feast your eyes on the cutest kittens from around the world!
34 |
35 |
36 |
37 |
38 | Latest Blog Posts
39 |
40 | "The First 30 Days with Your New Kitten"
41 | "How to Kitten-Proof Your Home"
42 | "Best DIY Toys for Kittens"
43 |
44 |
45 |
46 |
47 | Testimonials
48 |
49 | "KittenLand is my go-to resource for everything kitten-related. The care guides are incredibly helpful!"
50 | — Jane D., Proud Kitten Owner
51 |
52 |
53 | "I adopted my first kitten last month, and the tips on this website have been a lifesaver!"
54 | — Mark S., New Kitten Parent
55 |
56 |
57 |
58 |
59 | Get Involved
60 | Join our community of kitten lovers! Follow us on social media, subscribe to our newsletter, or contribute to our blog. We'd love to have you as part of the KittenLand family!
61 |
62 |
63 |
64 | Contact Us
65 | Got questions? We've got answers! Reach out to us at email@example.com or call us at (555) KIT-TENS.
66 | 🐾 Thank you for visiting KittenLand! 🐾
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------