-
Notifications
You must be signed in to change notification settings - Fork 195
Expand file tree
/
Copy pathdashboard.html
More file actions
210 lines (188 loc) · 8.47 KB
/
dashboard.html
File metadata and controls
210 lines (188 loc) · 8.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
<!DOCTYPE html>
<html lang="th">
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script
async
src="https://www.googletagmanager.com/gtag/js?id=G-N6Z3QPSTBV"
></script>
<script>
window.dataLayer = window.dataLayer || []
function gtag() {
dataLayer.push(arguments)
}
gtag("js", new Date())
gtag("set", "allow_google_signals", false)
gtag("config", "G-N6Z3QPSTBV")
</script>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>webring dashboard</title>
<link rel="icon" href="webring.svg" />
<meta name="robots" content="noindex" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous"
/>
</head>
<body>
<div class="container px-4 py-5">
<h1 class="h3 pb-2 border-bottom">webring dashboard</h1>
<div x-data="dashboard">
<h2 class="h4 pt-3 pb-2 border-bottom">Site list</h2>
<div class="mb-3">
<button class="btn btn-primary" @click="generateIssue" :disabled="sitesForCleanup.length === 0">
Generate Cleanup Issue <span x-show="sitesForCleanup.length > 0" class="badge bg-danger ms-1" x-text="sitesForCleanup.length"></span>
</button>
</div>
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Number</th>
<th scope="col">Site</th>
<th scope="col">Owner</th>
<th scope="col">Notes</th>
</tr>
</thead>
<tbody>
<template x-for="{site, id} in sites" :key="id">
<tr :class="hasCleanupIssues(site) ? 'table-danger' : ''">
<th scope="row" x-text="site.number"></th>
<td>
<a :href="site.url" target="_blank" x-text="id"></a>
</td>
<td>
<a x-show="site.owner" :href="'https://github.com/' + site.owner" target="_blank" x-text="site.owner"></a>
</td>
<td>
<span x-text="notes(site)"></span>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
<script>
document.addEventListener("alpine:init", () => {
Alpine.data("dashboard", () => ({
sites: [],
// Constants for issue detection
BACKLINK_MISSING_DAYS_THRESHOLD: 7, // Days without backlink to qualify for cleanup
UPDATE_DAYS_WARNING: 3, // Days without update to show warning
async init() {
const response = await fetch(
"https://wonderfulsoftware.github.io/webring-site-data/data.json"
)
if (!response.ok) {
throw new Error("Unable to fetch site data")
}
const data = await response.json()
const sites = Object.entries(data)
.map(([id, site]) => ({
id,
site,
}))
.filter(({ site }) => site.number)
.sort((a, b) => a.site.number - b.site.number)
console.log(sites)
this.sites = sites
},
// Get all issues for a site with structured information
getIssues(site) {
const issues = []
// Check for backlink
if (!site.backlink) {
issues.push({
message: "unable to detect backlink",
isCleanupCandidate: true
})
}
// Check for site not updated recently
if (site.lastUpdated) {
const lastUpdated = Date.parse(site.lastUpdated)
const daysSinceUpdate = Math.floor((Date.now() - lastUpdated) / (1000 * 60 * 60 * 24))
if (daysSinceUpdate >= this.UPDATE_DAYS_WARNING) {
issues.push({
message: `not updated in ${daysSinceUpdate} days`,
// Site is a cleanup candidate if missing backlink for 7+ days
isCleanupCandidate: daysSinceUpdate >= this.BACKLINK_MISSING_DAYS_THRESHOLD
})
}
}
return issues
},
// Format issues as a string
notes(site) {
return this.getIssues(site).map(issue => issue.message).join(", ")
},
// Check if a site has cleanup-worthy issues
hasCleanupIssues(site) {
// A site qualifies for cleanup if it has at least one issue that makes it a cleanup candidate
// AND it must have a missing backlink issue
const issues = this.getIssues(site)
return issues.some(issue => issue.isCleanupCandidate) && !site.backlink
},
// Computed property to get all sites with cleanup issues
get sitesForCleanup() {
return this.sites.filter(({site}) => this.hasCleanupIssues(site))
},
// Generate and open GitHub issue
generateIssue() {
if (this.sitesForCleanup.length === 0) return
// Set removal date to 30 days from now
const removalDate = new Date()
removalDate.setDate(removalDate.getDate() + 30)
const formattedRemovalDate = removalDate.toISOString().split('T')[0]
// Generate issue title
const title = `webring cleanup on ${formattedRemovalDate}`
// Generate issue body
let body = `Hello! 👋\n\n`
body += `As part of occasional housekeeping, which includes tidying up the webring, I will be removing sites from the webring that no longer has a backlink. This has been scheduled to take place on ${formattedRemovalDate}.\n\n`
body += `The following table lists the websites that are missing a backlink:\n\n`
// Create a table with site information
body += `| Site ID | Site Owner | Remark |\n`
body += `| --- | --- | --- |\n`
this.sitesForCleanup.forEach(({id, site}) => {
const issues = this.getIssues(site);
const remarks = issues.map(issue => issue.message).join(", ");
body += `| ${id} | ${site.owner ? `@${site.owner}` : ''} | ${remarks} |\n`
})
body += `\n- If you are on this list and would like to continue your participation on the webring, please make sure your site is accessible and has a backlink by ${new Date(removalDate.getTime() - 86400000).toISOString().split('T')[0]}. I will re-check the websites again by that time. Thank you for your continued support.\n\n`
body += `- Otherwise, no action is required, and thank you for your participation on this webring project.\n\n`
body += `Regardless of whether you choose to continue participating or not, your participation helped us grow this webring by a lot, and I appreciated it very much!\n\n`
body += `If you need help adding the backlink, you can use this HTML code on your website:\n`
body += `\`\`\`html\n<a href="https://webring.wonderful.software#YOUR.SITE.ID">วงแหวนเว็บ</a>\n\`\`\`\n`
body += `(Replace \`YOUR.SITE.ID\` with your site ID from the table above)\n`
// Create URL to GitHub issue creation page with prefilled values
const issueUrl = new URL("https://github.com/wonderfulsoftware/webring/issues/new")
issueUrl.searchParams.set("title", title)
issueUrl.searchParams.set("body", body)
issueUrl.searchParams.set("labels", "cleanup")
// Open in a new tab
window.open(issueUrl.toString(), "_blank")
}
}))
})
</script>
<script
defer
src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"
></script>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"
></script>
<!-- Cloudflare Web Analytics -->
<script
defer
src="https://static.cloudflareinsights.com/beacon.min.js"
data-cf-beacon='{"token": "4f95ad66bda34896b8c3147ecc0510ca"}'
></script>
<!-- End Cloudflare Web Analytics -->
</body>
</html>