<h1 style="color:white"> 🐮 MooBoard</h1>
---
[MOOBOARD](https://mootwo.fly.dev/)
---
<h1 style="font-family:'American Typewriter'">
Imperative v. Declarative
</h1>
We used both :shrug: and more!
---
Our routes were mainly OOP:
```javascript=1
const insert_post = db.prepare(/*sql*/ `
INSERT INTO posts (img_url, caption, user_id)
VALUES ($image_url, $caption, $user_id)
`);
const createPost = (image_url, caption, user_id) => {
insert_post.run({ image_url, caption, user_id });
};
```
Our model files follow the object-oriented programming (OOP) paradigm, where the code is organised around objects and their interactions. The db object is used as an instance of the sqlite database class, and various methods are defined to interact with the database.
---
```javascript=1
const post = (req, res) => {
const { email, password } = req.body;
const user = getUserByEmail(email);
if (!email || !password || !user) {
res.redirect(
`/log-in?error=login%20fail&email=${email.replace('@', '%40')}`
);
return;
}
bcrypt.compare(password, user.password_hash).then((result) => {
if (!result) {
res.status(400).send(/*html*/ `
<h1>Login failed</h1>
<p>The email address or password you entered is incorrect. Please try again.</p>
<br>
<p>Click <a href="/">here</a> to return to the homepage</p>
<br>
<p>Or click <a href="/sign-up">here</a> to create a new account.</p>`);
} else {
const sessionID = createSession(user.id);
res.cookie('sid', sessionID, {
signed: true,
maxAge: 1000 * 60 * 60 * 24 * 7,
sameSite: 'lax',
httpOnly: true,
});
res.redirect('/');
}
});
};
```
Our routes followed the imperative programming paradigm, where statements are executed sequentially to perform a series of operations.
---
```javascript=1
function homePage(req) {
const userId = req.session && req.session.user_id;
const posts = listPosts();
const userPosts = posts.filter((post) => {
return post.user_id === userId;
});
const postEls = userPosts
.map((post) => {
return /*html*/ `
<figure class="grid-item">
<form action="/delete/${post.id}" method="post" class="grid-item__delete-button-form">
<button type="submit" class="grid-item__delete-button"> ❌
</button>
</form>
<img src=${post.img_url} class="grid-item__image" />
<figcaption class="grid-item__caption">${post.caption}</figcaption>
</figure>
`;
})
.join('');
return /*html*/ `
<header class="header row space-between align-items-center">
<p class="header__logo">🐮</p>
<form action='/log-out' method="POST">
<button type="submit" class="header__button button">Log out</button>
</form>
</header>
<main>
<section class="grid-container">
${postEls}
</section>
<section id="uploadForm" class="upload-form__container">
<form action="/" method="post" class="upload-form row column align-items-center" enctype="multipart/form-data">
<label for="imageURL" class="upload-form__label stack-sm">Enter an image URL</label>
<input type="file" accept=".png, .jpeg, .jpg, .gif" id="imageURL" name="image" class="upload-form__input form-input stack-md" required>
<label for="caption" class="upload-form__label stack-sm">Enter a caption</label>
<textarea id="caption" name="caption" class="upload-form__textarea form-input stack-md" rows="4" cols="50" maxlength="100" required></textarea>
<button type="submit" class="upload-form__submit-button button">Submit</button>
</form>
</section>
<button class="form-toggle" id="formToggle" onclick="
const form = document.getElementById('uploadForm');
if (form.style.display === 'block') form.style.display = 'none';
else form.style.display = 'block';">
<span class="button-content">+</span>
</button>
</main>
`;
}
```
Our homepage template function uses imperative programming to iterate over the user's posts and generate HTML elements accordingly. It also follows a declarative programming approach by defining the HTML structure using template literals.
---
<h1 style="color:turquoise">Facilitator</h1>
From the checklist:
<ul style="color:purple">
<li>Stand up stand ups</li>
<li> 🏃♀️ quick "sprints"</li>
<li><span style="color:yellow">MVP!</span> and another <span style="color:yellow">MVP!</span> and another!</li>
<li>stay on top of project board</li>
</ul>
---
[PROJECT BOARD!!!!](https://github.com/orgs/fac27/projects/27/views/2)

---
<h1 style="color:turquoise">DevOps</h1>
<p>
<b>It doesn't work 💩</b>
</p>

---
<h1 style="color:turquoise">UX</h1>
[wireframe](https://www.figma.com/file/sUkQHU8LYH3RX8R8q8rqtz/Kudos-Meow-Board?type=design&node-id=0-1&t=9RHBPiubStFi3yuG-0)
- User journey could have been discussed earlier
- Site not fully responsive
<img src="https://hackmd.io/_uploads/S1jk0VyP2.png" height="200px">
---
<h1 style="color:turquoise">QA & Testing</h1>
<ul style="color:yellow">
<li>Well one of them works 🤷♂️</li>
<li>Had trouble reconciling multer form with test form</li>
<li>Pretty happy about eslint/prettier/husky setup</li>
</ul>
---
<h1 style="color:turquoise">Zak Code Snippet</h1>
---
From this
```javascript
const post = (req, res) => {
const { caption } = req.body;
const { path, originalname } = req.file;
const extension = originalname.split('.')[1];
const here = __dirname.split('/').slice(0,-2).join('/');
const filePath = `${here}/${path}.${extension}`;
console.log(req.file);
createPost(filePath, caption, 1);
res.redirect('/');
};
```
---
To this:
```javascript
const post = (req, res) => {
const { caption } = req.body;
const imageUrl = req.file.filename;
const userId = req.session && req.session.user_id;
createPost(imageUrl, sanitise(caption), userId);
res.redirect('/');
};
```
---
<h1 style="color:turquoise">Cameo Code Snippet</h1>
---
```javascript=1
const userForm = (title, error, email) => {
return
<header class="header row space-between align-items-center">
<h1> ${title} to add your moo'd</h1>
</header>
<section>
<form action=${title} method="post" class="upload-form row column align-items-center">
<label for="email" class="upload-form__label stack-sm" >Email</label>
<input type="email" id="email" name="email" value = "${
error ? email : ''
}" class="upload-form__input form-input stack-md" required>
<label for="password" class="upload-form__label stack-sm">Enter password</label>
<input type="password" id="password" name="password" class="upload-form__textarea form-input stack-md" required>
${error ? `<p class="error">${error} </p>` : ''}
<button type="submit" class="upload-form__submit-button button">Submit</button>
</form>
</section>
;
};
```
---
<h1 style="color:turquoise">Simon Code Snippet</h1>
```javascript=1
async function getFigureCount() {
const { body } = await request('/', {
method: 'GET',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
});
const regex = new RegExp(`<figure\\b`, 'gi');
const matches = body.match(regex) || [];
return matches.length;
}
function postBodyTest() {
test('should add post to page body', async () => {
const firstFigureCount = await getFigureCount();
await request('/', {
method: 'POST',
body: {
imageURL:
'https://upload.wikimedia.org/wikipedia/commons/b/b6/Image_created_with_a_mobile_phone.png',
caption: 'This is my test caption',
},
});
const secondFigureCount = await getFigureCount();
assert.equal(firstFigureCount + 1, secondFigureCount);
});
}
```
---
<h1 style="color:turquoise">Alphonso Code Snippet</h1>
```javascript=1
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, process.env.ASSETS);
},
filename: (req, file, cb) => {
const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
const extension = file.originalname.split('.')[1];
cb(null, `${file.fieldname}-${uniqueSuffix}.${extension}`);
},
});
```
---
Let's take a look...
{"metaMigratedAt":"2023-06-17T19:15:08.614Z","metaMigratedFrom":"Content","title":"Untitled","breaks":true,"contributors":"[{\"id\":\"c769ecbf-3e26-43a0-a776-f83bab6e212a\",\"add\":6069,\"del\":950},{\"id\":\"e4684b11-975d-4d26-a460-4cea3ac180e8\",\"add\":2453,\"del\":2825},{\"id\":null,\"add\":766,\"del\":12},{\"id\":\"eeea763e-8731-4434-88ff-28dee8eb171b\",\"add\":571,\"del\":8},{\"id\":\"c5c54e30-9692-49dc-baf9-f45297507939\",\"add\":894,\"del\":15},{\"id\":\"5a3f4bdd-9e0a-4a00-b0eb-3ee951877df7\",\"add\":497,\"del\":64},{\"id\":\"fabff5d3-4757-44f9-9d3c-6ea0d5d4251f\",\"add\":1152,\"del\":76}]"}