Skip to content

Commit cf2e6dc

Browse files
committed
Add feedback functionality support.
1 parent 501b1e2 commit cf2e6dc

File tree

6 files changed

+282
-1
lines changed

6 files changed

+282
-1
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package custom.application.v1;
2+
3+
import custom.objects.Feedback;
4+
import custom.objects.User;
5+
import custom.util.AuthenticationService;
6+
import org.tinystruct.AbstractApplication;
7+
import org.tinystruct.ApplicationException;
8+
import org.tinystruct.data.component.Table;
9+
import org.tinystruct.data.component.Builder;
10+
import org.tinystruct.http.Request;
11+
import org.tinystruct.http.Response;
12+
import org.tinystruct.http.ResponseStatus;
13+
import org.tinystruct.system.annotation.Action;
14+
15+
import java.util.Date;
16+
17+
public class feedback extends AbstractApplication {
18+
@Override
19+
public void init() {
20+
}
21+
22+
@Override
23+
public String version() {
24+
return "1.0";
25+
}
26+
27+
@Action("feedback/submit")
28+
public String submit(Request request, Response response) {
29+
String content = request.getParameter("content");
30+
if (content == null || content.trim().isEmpty()) {
31+
response.setStatus(ResponseStatus.BAD_REQUEST);
32+
return "{\"success\":false,\"message\":\"Feedback content is required.\"}";
33+
}
34+
Feedback feedback = new Feedback();
35+
feedback.setContent(content.trim());
36+
feedback.setCreatedAt(new Date());
37+
Object userId = request.getSession().getAttribute("user_id");
38+
if (userId != null) {
39+
try {
40+
feedback.setUserId(Integer.parseInt(userId.toString()));
41+
} catch (Exception ignored) {
42+
}
43+
}
44+
try {
45+
boolean status = feedback.append();
46+
return "{\"success\":" + status + "}";
47+
} catch (Exception e) {
48+
response.setStatus(ResponseStatus.INTERNAL_SERVER_ERROR);
49+
return "{\"success\":false,\"message\":\"Failed to save feedback.\"}";
50+
}
51+
}
52+
53+
@Action("feedback/list")
54+
public String list(Request request, Response response) throws ApplicationException {
55+
// Only admin can view
56+
Object userId = request.getSession().getAttribute("user_id");
57+
if (userId == null) {
58+
response.setStatus(ResponseStatus.UNAUTHORIZED);
59+
return "{\"error\":\"not_authenticated\"}";
60+
}
61+
User user = AuthenticationService.getCurrentUser(userId.toString());
62+
if (user == null || !user.getIsAdmin()) {
63+
response.setStatus(ResponseStatus.FORBIDDEN);
64+
return "{\"error\":\"forbidden\"}";
65+
}
66+
Feedback feedback = new Feedback();
67+
Table all = feedback.orderBy(new String[]{"created_at"}).findAll();
68+
StringBuilder json = new StringBuilder();
69+
json.append("[\n");
70+
for (int i = 0; i < all.size(); i++) {
71+
Feedback f = new Feedback();
72+
f.setData(all.get(i));
73+
json.append(f);
74+
if (i < all.size() - 1) json.append(",\n");
75+
}
76+
json.append("\n]");
77+
return json.toString();
78+
}
79+
80+
private String escapeJson(String s) {
81+
return s == null ? "" : s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "");
82+
}
83+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package custom.objects;
2+
3+
import org.tinystruct.data.component.AbstractData;
4+
import org.tinystruct.data.component.Row;
5+
import java.io.Serializable;
6+
import java.util.Date;
7+
8+
public class Feedback extends AbstractData implements Serializable {
9+
private static final long serialVersionUID = 1L;
10+
11+
private String content;
12+
private Date createdAt;
13+
private Integer userId;
14+
15+
public String getId() {
16+
return String.valueOf(this.Id);
17+
}
18+
19+
public void setContent(String content) {
20+
this.content = this.setFieldAsString("content", content);
21+
}
22+
23+
public String getContent() {
24+
return this.content;
25+
}
26+
27+
public void setCreatedAt(Date createdAt) {
28+
this.createdAt = this.setFieldAsDate("createdAt", createdAt);
29+
}
30+
31+
public Date getCreatedAt() {
32+
return this.createdAt;
33+
}
34+
35+
public void setUserId(Integer userId) {
36+
this.userId = this.setFieldAsInt("userId", userId);
37+
}
38+
39+
public Integer getUserId() {
40+
return this.userId;
41+
}
42+
43+
@Override
44+
public void setData(Row row) {
45+
if (row.getFieldInfo("id") != null) this.setId(row.getFieldInfo("id").stringValue());
46+
if (row.getFieldInfo("content") != null) this.setContent(row.getFieldInfo("content").stringValue());
47+
if (row.getFieldInfo("created_at") != null) this.setCreatedAt(row.getFieldInfo("created_at").dateValue());
48+
if (row.getFieldInfo("user_id") != null) this.setUserId(row.getFieldInfo("user_id").intValue());
49+
}
50+
51+
@Override
52+
public String toString() {
53+
StringBuilder buffer = new StringBuilder();
54+
buffer.append("{");
55+
buffer.append("\"id\":\"").append(this.getId()).append("\"");
56+
buffer.append(",\"content\":\"").append(this.getContent() != null ? this.getContent().replace("\"", "\\\"") : "").append("\"");
57+
buffer.append(",\"createdAt\":\"").append(this.getCreatedAt()).append("\"");
58+
buffer.append(",\"userId\":").append(this.getUserId());
59+
buffer.append("}");
60+
return buffer.toString();
61+
}
62+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<mapping>
4+
<class name="Feedback" table="feedback">
5+
<id name="Id" column="id" increment="true" generate="true" length="20" type="INTEGER"/>
6+
<property name="content" column="content" length="0" type="TEXT"/>
7+
<property name="createdAt" column="created_at" length="0" type="DATE"/>
8+
<property name="userId" column="user_id" length="20" type="INTEGER"/>
9+
</class>
10+
</mapping>

src/main/resources/init.sql

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,13 @@ AFTER UPDATE ON user_prompts
9393
FOR EACH ROW
9494
BEGIN
9595
UPDATE user_prompts SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
96-
END
96+
END
97+
98+
DROP TABLE IF EXISTS feedback;
99+
CREATE TABLE IF NOT EXISTS feedback (
100+
id INTEGER PRIMARY KEY AUTOINCREMENT,
101+
content TEXT NOT NULL,
102+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
103+
user_id INTEGER,
104+
FOREIGN KEY (user_id) REFERENCES users(id)
105+
);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Feedback</title>
7+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
8+
<style>
9+
.container { max-width: 600px; margin: 40px auto; padding: 20px; }
10+
textarea { min-height: 120px; }
11+
</style>
12+
</head>
13+
<body>
14+
<div class="container">
15+
<h2>Feedback</h2>
16+
<form id="feedback-form">
17+
<div class="form-group">
18+
<label for="feedback-content">Your Feedback</label>
19+
<textarea class="form-control" id="feedback-content" name="content" required placeholder="Enter your feedback here..."></textarea>
20+
</div>
21+
<div class="alert alert-success" id="feedback-success" style="display:none;">Thank you for your feedback!</div>
22+
<div class="alert alert-danger" id="feedback-error" style="display:none;"></div>
23+
<button type="submit" class="btn btn-primary">Submit</button>
24+
</form>
25+
</div>
26+
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
27+
<script>
28+
$('#feedback-form').submit(function(e) {
29+
e.preventDefault();
30+
var content = $('#feedback-content').val();
31+
$('#feedback-success').hide();
32+
$('#feedback-error').hide();
33+
$.ajax({
34+
url: '/?q=feedback/submit',
35+
type: 'POST',
36+
data: { content: content },
37+
dataType: 'json',
38+
success: function(response) {
39+
if (response.success) {
40+
$('#feedback-success').show();
41+
$('#feedback-content').val('');
42+
} else {
43+
$('#feedback-error').text(response.message || 'Failed to submit feedback.').show();
44+
}
45+
},
46+
error: function(xhr) {
47+
$('#feedback-error').text('Failed to submit feedback.').show();
48+
}
49+
});
50+
});
51+
</script>
52+
</body>
53+
</html>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Feedback Admin</title>
7+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
8+
<style>
9+
.container { max-width: 900px; margin: 40px auto; padding: 20px; }
10+
table { margin-top: 20px; }
11+
td { word-break: break-all; }
12+
</style>
13+
</head>
14+
<body>
15+
<div class="container">
16+
<h2>User Feedback</h2>
17+
<table class="table table-bordered table-striped" id="feedback-table">
18+
<thead>
19+
<tr>
20+
<th>ID</th>
21+
<th>Content</th>
22+
<th>Created At</th>
23+
<th>User ID</th>
24+
</tr>
25+
</thead>
26+
<tbody>
27+
<tr><td colspan="4" class="text-center">Loading...</td></tr>
28+
</tbody>
29+
</table>
30+
<div class="alert alert-danger" id="feedback-error" style="display:none;"></div>
31+
</div>
32+
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
33+
<script>
34+
function loadFeedback() {
35+
$.ajax({
36+
url: '/?q=feedback/list',
37+
type: 'GET',
38+
dataType: 'json',
39+
success: function(data) {
40+
var tbody = '';
41+
if (data.length === 0) {
42+
tbody = '<tr><td colspan="4" class="text-center">No feedback found.</td></tr>';
43+
} else {
44+
for (var i = 0; i < data.length; i++) {
45+
tbody += '<tr>' +
46+
'<td>' + data[i].id + '</td>' +
47+
'<td>' + $('<div>').text(data[i].content).html() + '</td>' +
48+
'<td>' + data[i].created_at + '</td>' +
49+
'<td>' + (data[i].user_id !== null ? data[i].user_id : '-') + '</td>' +
50+
'</tr>';
51+
}
52+
}
53+
$('#feedback-table tbody').html(tbody);
54+
},
55+
error: function(xhr) {
56+
$('#feedback-error').text('Failed to load feedback.').show();
57+
$('#feedback-table tbody').html('<tr><td colspan="4" class="text-center">Error loading feedback.</td></tr>');
58+
}
59+
});
60+
}
61+
$(function() { loadFeedback(); });
62+
</script>
63+
</body>
64+
</html>

0 commit comments

Comments
 (0)