Merge pull request 'bunch of newness' (#2) from feat-input into main
Reviewed-on: #2
This commit is contained in:
commit
af6888a7bb
11 changed files with 4846 additions and 16 deletions
2
changelog-app-ui/.gitignore
vendored
2
changelog-app-ui/.gitignore
vendored
|
@ -22,3 +22,5 @@ dist-ssr
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
__snapshots__/
|
||||||
|
|
4735
changelog-app-ui/package-lock.json
generated
4735
changelog-app-ui/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -29,6 +29,7 @@
|
||||||
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.9",
|
"eslint-plugin-react-refresh": "^0.4.9",
|
||||||
"globals": "^15.9.0",
|
"globals": "^15.9.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
"jsdom": "^25.0.0",
|
"jsdom": "^25.0.0",
|
||||||
"vite": "^5.4.1",
|
"vite": "^5.4.1",
|
||||||
"vitest": "^2.0.5"
|
"vitest": "^2.0.5"
|
||||||
|
|
|
@ -20,29 +20,40 @@ function App() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is just temp data that will get updated later
|
||||||
|
//
|
||||||
|
const fakeDate = new Date();
|
||||||
|
fakeDate.setDate(fakeDate.getDate() - 1);
|
||||||
|
const fakeDate2 = new Date();
|
||||||
|
fakeDate2.setDate(fakeDate2.getDate() - 2);
|
||||||
|
const fakeDate3 = new Date();
|
||||||
|
fakeDate3.setDate(fakeDate3.getDate() - 3);
|
||||||
|
const fakeDate4 = new Date();
|
||||||
|
fakeDate4.setDate(fakeDate4.getDate() - 4);
|
||||||
|
|
||||||
const tempData = [
|
const tempData = [
|
||||||
{
|
{
|
||||||
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae nibh velit. Vivamus maximus elit et eleifend hendrerit. Praesent ac metus eget risus accumsan finibus. Cras tempor dignissim dolor, ut tempus tortor interdum non. Sed sit amet fringilla turpis. Sed congue feugiat orci, vel iaculis libero venenatis eu.",
|
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae nibh velit. Vivamus maximus elit et eleifend hendrerit. Praesent ac metus eget risus accumsan finibus. Cras tempor dignissim dolor, ut tempus tortor interdum non. Sed sit amet fringilla turpis. Sed congue feugiat orci, vel iaculis libero venenatis eu.",
|
||||||
topics: ["kubernetes", "argocd"],
|
topics: ["#kubernetes", "#argocd"],
|
||||||
date: new Date().toLocaleDateString(),
|
date: fakeDate,
|
||||||
id: 1,
|
id: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae nibh velit. Vivamus maximus elit et eleifend hendrerit. Praesent ac metus eget risus accumsan finibus. Cras tempor dignissim dolor, ut tempus tortor interdum non. Sed sit amet fringilla turpis. Sed congue feugiat orci, vel iaculis libero venenatis eu.",
|
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae nibh velit. Vivamus maximus elit et eleifend hendrerit. Praesent ac metus eget risus accumsan finibus. Cras tempor dignissim dolor, ut tempus tortor interdum non. Sed sit amet fringilla turpis. Sed congue feugiat orci, vel iaculis libero venenatis eu.",
|
||||||
topics: ["react", "python", "javascript"],
|
topics: ["#react", "#python", "#javascript"],
|
||||||
date: new Date().toLocaleDateString(),
|
date: fakeDate2,
|
||||||
id: 2,
|
id: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae nibh velit. Vivamus maximus elit et eleifend hendrerit. Praesent ac metus eget risus accumsan finibus. Cras tempor dignissim dolor, ut tempus tortor interdum non. Sed sit amet fringilla turpis. Sed congue feugiat orci, vel iaculis libero venenatis eu.",
|
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae nibh velit. Vivamus maximus elit et eleifend hendrerit. Praesent ac metus eget risus accumsan finibus. Cras tempor dignissim dolor, ut tempus tortor interdum non. Sed sit amet fringilla turpis. Sed congue feugiat orci, vel iaculis libero venenatis eu.",
|
||||||
topics: ["docker", "proxmox"],
|
topics: ["#docker", "#proxmox"],
|
||||||
date: new Date().toLocaleDateString(),
|
date: fakeDate3,
|
||||||
id: 3,
|
id: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae nibh velit. Vivamus maximus elit et eleifend hendrerit. Praesent ac metus eget risus accumsan finibus. Cras tempor dignissim dolor, ut tempus tortor interdum non. Sed sit amet fringilla turpis. Sed congue feugiat orci, vel iaculis libero venenatis eu.",
|
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae nibh velit. Vivamus maximus elit et eleifend hendrerit. Praesent ac metus eget risus accumsan finibus. Cras tempor dignissim dolor, ut tempus tortor interdum non. Sed sit amet fringilla turpis. Sed congue feugiat orci, vel iaculis libero venenatis eu.",
|
||||||
topics: ["aws", "github"],
|
topics: ["#aws", "#github"],
|
||||||
date: new Date().toLocaleDateString(),
|
date: fakeDate4,
|
||||||
id: 4,
|
id: 4,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -2,33 +2,46 @@ import PropTypes from "prop-types";
|
||||||
import styles from "./changeLogInput.module.css";
|
import styles from "./changeLogInput.module.css";
|
||||||
|
|
||||||
const ChangeLogInput = (props) => {
|
const ChangeLogInput = (props) => {
|
||||||
const handleClick = (e) => {
|
const handleClick = () => {
|
||||||
|
const topics = getTopics(props.logInput);
|
||||||
|
|
||||||
const newEntry = {
|
const newEntry = {
|
||||||
body: props.logInput,
|
body: props.logInput,
|
||||||
topics: ["test"],
|
topics: topics,
|
||||||
date: new Date().toLocaleDateString(),
|
date: new Date(),
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
};
|
};
|
||||||
|
|
||||||
props.setFeed([newEntry, ...props.feed]);
|
props.setFeed([newEntry, ...props.feed]);
|
||||||
props.setLogInput("");
|
props.setLogInput("");
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<textarea
|
<textarea
|
||||||
rows="1"
|
rows="1"
|
||||||
placeholder="What did you change..."
|
placeholder="What did you change...?!"
|
||||||
className={styles.input}
|
className={styles.input}
|
||||||
onChange={(e) => props.setLogInput(e.target.value)}
|
onChange={(e) => props.setLogInput(e.target.value)}
|
||||||
value={props.logInput}
|
value={props.logInput}
|
||||||
/>
|
/>
|
||||||
<button onClick={handleClick} className={styles.btn}>
|
<button onClick={handleClick} className={styles.btn}>
|
||||||
Submit
|
Log it!
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getTopics = (body) => {
|
||||||
|
const re = /#\w+/g;
|
||||||
|
let topics = [];
|
||||||
|
const matchs = [...body.matchAll(re)];
|
||||||
|
|
||||||
|
matchs.forEach((match) => (topics = topics.concat(match[0])));
|
||||||
|
|
||||||
|
return topics;
|
||||||
|
};
|
||||||
|
|
||||||
ChangeLogInput.propTypes = {
|
ChangeLogInput.propTypes = {
|
||||||
setLogInput: PropTypes.func,
|
setLogInput: PropTypes.func,
|
||||||
logInput: PropTypes.string,
|
logInput: PropTypes.string,
|
||||||
|
|
|
@ -8,6 +8,12 @@
|
||||||
.input {
|
.input {
|
||||||
outline: 1px solid black;
|
outline: 1px solid black;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
field-sizing: content;
|
||||||
|
/*min-height: 17px;*/
|
||||||
|
/*border: 1px solid #ccc;*/
|
||||||
|
/*max-height: 150px;*/
|
||||||
|
/*overflow-x: hidden;*/
|
||||||
|
/*overflow-y: auto;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { HashTagLink } from "./links";
|
import { dateParser } from "../utils/dateParser";
|
||||||
|
import { HashTagLink } from "../utils/links";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import styles from "./feed.module.css";
|
import styles from "./feed.module.css";
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ const LogEntryCard = ({ body, date, topics }) => {
|
||||||
<p>{body}</p>
|
<p>{body}</p>
|
||||||
<div className={styles.cardBottom}>
|
<div className={styles.cardBottom}>
|
||||||
<FeedTopics topics={topics} />
|
<FeedTopics topics={topics} />
|
||||||
<p>{date}</p>
|
<p>{dateParser(date)}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -51,4 +52,5 @@ LogEntryCard.propTypes = {
|
||||||
topics: PropTypes.array,
|
topics: PropTypes.array,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//<p>{date.toLocaleString()}</p>
|
||||||
export default Feed;
|
export default Feed;
|
||||||
|
|
5
changelog-app-ui/src/components/topics.jsx
Normal file
5
changelog-app-ui/src/components/topics.jsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
const Topic = (props) => {
|
||||||
|
const entries = props.entries;
|
||||||
|
|
||||||
|
const filtered = entries.filter((item) => {});
|
||||||
|
};
|
24
changelog-app-ui/src/utils/dateParser.js
Normal file
24
changelog-app-ui/src/utils/dateParser.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
function dateParser(date) {
|
||||||
|
/* Returns a time/date formatted string
|
||||||
|
* based on the age of the log entry.
|
||||||
|
* > 24 hrs return the date
|
||||||
|
* < 24 hrs > 1 hr return the number of hours
|
||||||
|
* otherwise return the minutes since the log was entered
|
||||||
|
*/
|
||||||
|
const currentTime = new Date();
|
||||||
|
|
||||||
|
const difference = Math.floor((currentTime - date) / 1000);
|
||||||
|
|
||||||
|
if (difference >= 86400) {
|
||||||
|
// Return date since entry is older than a day
|
||||||
|
return date.toLocaleString();
|
||||||
|
} else if (difference >= 3600) {
|
||||||
|
// Return hours since log entry
|
||||||
|
return `${difference / 3600}h`;
|
||||||
|
} else {
|
||||||
|
// Reteurn Minutes since log entry
|
||||||
|
return `${Math.floor(difference / 60)}m`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { dateParser };
|
31
changelog-app-ui/src/utils/dateParser.test.js
Normal file
31
changelog-app-ui/src/utils/dateParser.test.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { dateParser } from "./dateParser";
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
|
||||||
|
describe("Date Parser", () => {
|
||||||
|
it("should have 0m for new entries", () => {
|
||||||
|
const currentTime = new Date();
|
||||||
|
const timeString = dateParser(currentTime);
|
||||||
|
expect(timeString).toMatch(/0m/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return (n)m for entries older than 30 minutes", () => {
|
||||||
|
const currentTime = new Date();
|
||||||
|
currentTime.setMinutes(currentTime.getMinutes() + 30);
|
||||||
|
const timeString = dateParser(currentTime);
|
||||||
|
expect(timeString).toMatch(/30m/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return (n)h for entires aged over an hour", () => {
|
||||||
|
const currentTime = new Date();
|
||||||
|
currentTime.setHours(currentTime.getHours() - 2);
|
||||||
|
const timeString = dateParser(currentTime);
|
||||||
|
expect(timeString).toMatch(/2h/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return the date for entries older than 24 hours", () => {
|
||||||
|
const currentTime = new Date();
|
||||||
|
currentTime.setDate(currentTime.getDate() - 1);
|
||||||
|
const timeString = dateParser(currentTime);
|
||||||
|
expect(timeString).toBeTypeOf("string");
|
||||||
|
});
|
||||||
|
});
|
|
@ -2,7 +2,7 @@ import { Link } from "react-router-dom";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
const HashTagLink = ({ tag }) => {
|
const HashTagLink = ({ tag }) => {
|
||||||
return <a href="#">#{tag}</a>;
|
return <a href="#">{tag}</a>;
|
||||||
};
|
};
|
||||||
|
|
||||||
HashTagLink.propTypes = {
|
HashTagLink.propTypes = {
|
Loading…
Reference in a new issue