Skip to content

Commit e6fcc15

Browse files
init. base changelog
1 parent 703374d commit e6fcc15

File tree

13 files changed

+3826
-480
lines changed

13 files changed

+3826
-480
lines changed

backend/src/main/java/io/easystartup/suggestfeature/beans/Changelog.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
@Document
2222
@JsonIgnoreProperties(ignoreUnknown = true)
2323
@CompoundIndexes({
24-
@CompoundIndex(name = "organizationId_1_createdAt_1", def = "{'organizationId': 1, 'createdAt': 1}"),
24+
@CompoundIndex(name = "organizationId_1_changelogDate_1", def = "{'organizationId': 1, 'changelogDate': 1}"),
2525
// Needed for search functionality Todo: Remove this index and implement search functionality using es/manticore
2626
@CompoundIndex(name = "organizationId_1_title_1", def = "{'organizationId': 1, 'title': 'text'}"),
2727
// For regex based search on title
@@ -35,16 +35,20 @@ public class Changelog {
3535
public static final String FIELD_CREATED_AT = "createdAt";
3636
public static final String FIELD_CHANGELOG_DATE = "changelogDate";
3737
public static final String FIELD_SLUG = "slug";
38+
public static final String FIELD_DRAFT = "draft";
3839

3940
@Id
4041
private String id;
4142
@NotBlank
4243
@Size(max = 500)
4344
private String title;
45+
4446
@NotBlank
4547
@Size(max = 100_000)
4648
private String content;
4749

50+
private String html;
51+
4852
private String coverImage;
4953
@Indexed
5054
private String organizationId;
@@ -79,19 +83,19 @@ public void setId(String id) {
7983
this.id = id;
8084
}
8185

82-
public @NotBlank @Size(max = 500) String getTitle() {
86+
public String getTitle() {
8387
return title;
8488
}
8589

86-
public void setTitle(@NotBlank @Size(max = 500) String title) {
90+
public void setTitle(String title) {
8791
this.title = title;
8892
}
8993

90-
public @NotBlank @Size(max = 100_000) String getContent() {
94+
public String getContent() {
9195
return content;
9296
}
9397

94-
public void setContent(@NotBlank @Size(max = 100_000) String content) {
98+
public void setContent(String content) {
9599
this.content = content;
96100
}
97101

@@ -182,4 +186,12 @@ public String getCoverImage() {
182186
public void setCoverImage(String coverImage) {
183187
this.coverImage = coverImage;
184188
}
189+
190+
public String getHtml() {
191+
return html;
192+
}
193+
194+
public void setHtml(String html) {
195+
this.html = html;
196+
}
185197
}

backend/src/main/java/io/easystartup/suggestfeature/dto/ChangelogUpdateDTO.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public class ChangelogUpdateDTO {
1717
private List<String> postIds;
1818
private String title;
1919
private String content;
20+
private String html;
2021
private List<String> tags;
2122
private Long changelogDate;
2223
private boolean draft;
@@ -88,4 +89,12 @@ public String getCoverImage() {
8889
public void setCoverImage(String coverImage) {
8990
this.coverImage = coverImage;
9091
}
92+
93+
public String getHtml() {
94+
return html;
95+
}
96+
97+
public void setHtml(String html) {
98+
this.html = html;
99+
}
91100
}

backend/src/main/java/io/easystartup/suggestfeature/rest/admin/ChangelogRestApi.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ public Response updateChangelogDetails(ChangelogUpdateDTO req) {
133133
existingChangelog.setContent(req.getContent());
134134
}
135135
existingChangelog.setPostIds(req.getPostIds());
136+
existingChangelog.setHtml(req.getHtml());
136137
existingChangelog.setModifiedAt(System.currentTimeMillis());
137138
existingChangelog.setDraft(req.isDraft());
138139
existingChangelog.setTags(req.getTags());

backend/src/main/java/io/easystartup/suggestfeature/rest/portal/unauth/PublicPortalChangelogRestApi.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public Response getChangelog(@Context HttpServletRequest request) {
5555
return Response.ok().entity(Collections.emptyList()).build();
5656
}
5757
Criteria criteriaDefinition = Criteria.where(Changelog.FIELD_ORGANIZATION_ID).is(org.getId());
58+
criteriaDefinition.and(Changelog.FIELD_DRAFT).ne(true);
5859
Query query = new Query(criteriaDefinition);
5960
query.with(Sort.by(Sort.Direction.DESC, Changelog.FIELD_CHANGELOG_DATE));
6061
List<Changelog> changelogs = mongoConnection.getDefaultMongoTemplate().find(query, Changelog.class);
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React from 'react';
2+
import { format } from 'date-fns';
3+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
4+
import { Badge } from '@/components/ui/badge';
5+
import { headers } from 'next/headers';
6+
7+
interface ChangelogItem {
8+
title: string;
9+
html: string;
10+
coverImage: string;
11+
changelogDate: number;
12+
tags: string[];
13+
}
14+
15+
async function getChangelogItems(): Promise<ChangelogItem[]> {
16+
const headersList = headers()
17+
const host = headersList.get('host') || 'localhost:3000'
18+
const protocol = 'https';
19+
const path = 'api/portal/unauth/changelog/get-changelog-posts';
20+
21+
const res = await fetch(`${protocol}://${host}/${path}`, { next: { revalidate: 10 } });
22+
23+
if (!res.ok) {
24+
throw new Error('Failed to fetch changelog items');
25+
}
26+
27+
return res.json();
28+
}
29+
30+
export default async function ChangelogList() {
31+
const changelogItems = await getChangelogItems();
32+
33+
return (
34+
<div className="space-y-8">
35+
{changelogItems.map((item, index) => (
36+
<Card key={index} className="overflow-hidden">
37+
<CardHeader className="pb-0">
38+
<div className="flex justify-between items-center">
39+
<CardTitle className="text-xl font-semibold">{item.title}</CardTitle>
40+
<div className="text-sm text-gray-500">
41+
{format(new Date(item.changelogDate), 'MMMM d, yyyy')}
42+
</div>
43+
</div>
44+
<div className="flex gap-2 mt-2">
45+
{item.tags.map((tag, tagIndex) => (
46+
<Badge key={tagIndex} variant="secondary">{tag}</Badge>
47+
))}
48+
</div>
49+
</CardHeader>
50+
<CardContent>
51+
{item.coverImage && (
52+
<div className="mb-4">
53+
<img
54+
src={item.coverImage}
55+
alt={item.title}
56+
className="rounded-lg object-cover w-full h-auto max-h-[400px]"
57+
/>
58+
</div>
59+
)}
60+
<div dangerouslySetInnerHTML={{ __html: item.html }} className="prose max-w-none" />
61+
</CardContent>
62+
</Card>
63+
))}
64+
</div>
65+
);
66+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Loading() {
2+
return <div>Loading changelog...</div>;
3+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Suspense } from 'react';
2+
import ChangelogList from './ChangelogList';
3+
import Loading from './loading';
4+
5+
export const dynamic = 'force-dynamic';
6+
7+
export default function ChangelogPage() {
8+
return (
9+
<div className="container mx-auto px-4 py-8">
10+
<h1 className="text-3xl font-bold mb-8">Changelog</h1>
11+
<Suspense fallback={<Loading />}>
12+
<ChangelogList />
13+
</Suspense>
14+
</div>
15+
);
16+
}

frontend/next.config.mjs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,10 @@ const nextConfig = {
33
output: "standalone",
44
webpack: (config, { isServer }) => {
55
if (!isServer) {
6-
config.resolve.fallback = {
7-
fs: false,
8-
net: false,
9-
tls: false,
10-
};
6+
// Ensure that all imports of 'yjs' resolve to the same instance
7+
config.resolve.alias['yjs'] = path.resolve(__dirname, 'node_modules/yjs')
118
}
12-
return config;
9+
return config
1310
},
1411

1512
typescript: {

0 commit comments

Comments
 (0)