Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 109 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,90 @@ import aboutGraphic3 from "./assets/ramalama-about-graphic-3.svg";
import aboutGraphic4 from "./assets/ramalama-about-graphic-4.svg";
import copySVG from "./assets/copy.svg";

import presentations from './presentations.json'
import presentations from "./presentations.json";

let installCode1 = "curl -fsSL https://ramalama.ai/install.sh | bash";
let installCode2 = "pip install ramalama";

const padNumber = (value) => String(value).padStart(2, "0");

const formatICSDate = (date, { utc = false } = {}) => {
const year = utc ? date.getUTCFullYear() : date.getFullYear();
const month = utc ? date.getUTCMonth() : date.getMonth();
const day = utc ? date.getUTCDate() : date.getDate();
const hours = utc ? date.getUTCHours() : date.getHours();
const minutes = utc ? date.getUTCMinutes() : date.getMinutes();
const seconds = utc ? date.getUTCSeconds() : date.getSeconds();

return (
year +
padNumber(month + 1) +
padNumber(day) +
"T" +
padNumber(hours) +
padNumber(minutes) +
padNumber(seconds) +
(utc ? "Z" : "")
);
};

const escapeICSText = (text) =>
text.replace(/\\/g, "\\\\").replace(/;/g, "\\;").replace(/,/g, "\\,").replace(/\n/g, "\\n");


const nextBiweeklyWednesday = () => {
const MS_PER_DAY = 24 * 60 * 60 * 1000;

const referenceDate = new Date(Date.UTC(2025, 10, 19, 15, 30));
const now = new Date();

const diffDays = Math.floor((now.getTime() - referenceDate.getTime()) / MS_PER_DAY);
const mod = diffDays % 14;
const daysToAdd = mod == 0 ? 0 : 14 - mod

const candidate = new Date(referenceDate.getTime() + (diffDays + daysToAdd) * MS_PER_DAY);

if (candidate <= now) { // Handling the day of
candidate.setUTCDate(candidate.getUTCDate() + 14);
}

return candidate;
};

const createCalendarLink = () => {
const start = nextBiweeklyWednesday();
const end = new Date(start.getTime() + 60 * 60000);
const dtStamp = new Date();
const MEETING_LOCATION= "https://discord.gg/MkCXuTRBUn"
const MEETING_DESCRIPTION =
"We host a public community and developer meetup on Discord every other week to discuss project direction and provide an open forum for users to get help, ask questions, and showcase new features.";

const icsContent = [
"BEGIN:VCALENDAR",
"VERSION:2.0",
"PRODID:-//RamaLama//Community Meetup//EN",
"CALSCALE:GREGORIAN",
"METHOD:PUBLISH",
"BEGIN:VEVENT",
`UID:${start.getTime()}@ramalama.ai`,
`SUMMARY:RamaLama Community / Developer Meetup`,
`DTSTAMP:${formatICSDate(dtStamp, { utc: true })}`,
`DTSTART:${formatICSDate(start)}`,
`DTEND:${formatICSDate(end)}`,
`LOCATION:${MEETING_LOCATION}`,
`DESCRIPTION:${escapeICSText(MEETING_DESCRIPTION)}`,
`URL:${MEETING_LOCATION}`,
"END:VEVENT",
"END:VCALENDAR",
].join("\r\n");

return `data:text/calendar;charset=utf-8,${encodeURIComponent(icsContent)}`;
};

/* -------------------------------- Main App Function -------------------------------- */
function App() {
const calendarLink = createCalendarLink();

return (
<>
<main>
Expand Down Expand Up @@ -185,6 +262,37 @@ function App() {
</div>
</div>

{/* ------------------------------ Community Section ------------------------------ */}
<div className="community viewport" id="community">
<div className="community-info">
<h1 className="community-header">Community / Developer Meetups</h1>
<p className="community-text" role="paragraph">
We host a public community and developer meetup on Discord every other week to
discuss project direction and provide an open forum for users to get help, ask
questions, and showcase new features.
</p>
<div className="community-links">
<a
href="https://discord.gg/cFyDXs9nS9"
target="_blank"
rel="nofollow noopener noreferrer"
>
<button className="button">Join on Discord</button>
</a>
<a
href="https://docs.google.com/document/d/1wiqn7ItKgc8BgyTUQ46eeY23ms_hWbkhAoiP9D1ClfY/edit?tab=t.0#heading=h.b1x47hb6d0pt"
target="_blank"
rel="nofollow noopener noreferrer"
>
<button className="button">Meeting Agenda</button>
</a>
<a href={calendarLink} download="ramalama-community-meetup.ics">
<button className="button">Add to Calendar</button>
</a>
</div>
</div>
</div>

{/* ------------------------------ Presentations Section ------------------------------ */}
<div className="presentation viewport" id="presentations">
<div className="presentation-info">
Expand Down
50 changes: 48 additions & 2 deletions src/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ ul.nav-links {
.install,
.demo,
.about,
.presentation {
.presentation,
.community {
display: flex;
justify-content: center;
align-items: center;
Expand Down Expand Up @@ -451,6 +452,51 @@ ul {
text-align: center;
}

/* ------------------------------ Community Section ------------------------------ */

.community {
padding: 6% 10%;
height: auto;
@media screen and (max-width: 768px) {
padding: 10%;
}
}

.community-info {
max-width: 60ch;
text-align: center;
}

.community-header {
margin-bottom: 1rem;
}

.community-text {
margin: 1rem 0 0;
font-size: 1.1rem;
line-height: 1.5;
}

.community-links {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 1rem;
margin-top: 2rem;
align-items: center;
justify-content: center;
@media screen and (max-width: 600px) {
flex-direction: column;
}
}

.community-links button {
width: auto;
min-width: 240px;
justify-content: center;
text-align: center;
}


/* ------------------------------ Other Styles ------------------------------ */

Expand Down Expand Up @@ -491,4 +537,4 @@ table a,
.presentation p a {
color: blue;
text-decoration: underline;
}
}