djs-builder 0.6.401 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +292 -43
- package/function/dash.js +638 -0
- package/function/giveaway.js +47 -10
- package/function/security.js +0 -0
- package/handler/starter.js +73 -38
- package/package.json +9 -4
- package/views/404.ejs +40 -0
- package/views/dashboard.ejs +894 -0
- package/views/giveaways.ejs +306 -0
- package/views/guild.ejs +576 -0
- package/views/index.ejs +419 -0
- package/views/levels.ejs +326 -0
- package/views/login.ejs +437 -0
package/function/giveaway.js
CHANGED
|
@@ -9,6 +9,7 @@ const giveawaySchema = new Schema({
|
|
|
9
9
|
hoster: { type: String, required: true },
|
|
10
10
|
endEmbed: { type: Object, required: true },
|
|
11
11
|
winnerCount: { type: Number, default: 1 },
|
|
12
|
+
prize: { type: String, required: true },
|
|
12
13
|
winners: Array,
|
|
13
14
|
paused: { type: Boolean, default: false },
|
|
14
15
|
pausedTime: Array,
|
|
@@ -19,6 +20,7 @@ const giveawaySchema = new Schema({
|
|
|
19
20
|
enum: ["reaction", "button"],
|
|
20
21
|
default: "reaction",
|
|
21
22
|
},
|
|
23
|
+
requiredRoles: { type: [String], default: [] },
|
|
22
24
|
|
|
23
25
|
users: { type: [String], default: [] },
|
|
24
26
|
});
|
|
@@ -32,6 +34,7 @@ async function Gstart({
|
|
|
32
34
|
endTime: endTime,
|
|
33
35
|
winers: winnerCount,
|
|
34
36
|
channelId: channelId,
|
|
37
|
+
prize: prize,
|
|
35
38
|
embed: {
|
|
36
39
|
custom: Scustom = false,
|
|
37
40
|
title: Stitle = false,
|
|
@@ -53,13 +56,15 @@ async function Gstart({
|
|
|
53
56
|
emoji: emoji = false,
|
|
54
57
|
label: label = false,
|
|
55
58
|
style: style = false,
|
|
56
|
-
id
|
|
59
|
+
id: id = false,
|
|
57
60
|
},
|
|
61
|
+
requirements: { requiredRoles: requiredRoles = [] } = {},
|
|
58
62
|
}) {
|
|
59
63
|
if (!winnerCount) winnerCount = 1;
|
|
60
64
|
if (!endTime) return { error: "End time is required." };
|
|
61
65
|
if (!channelId) return { error: "Channel ID is required." };
|
|
62
66
|
if (!context) return { error: "Context is required." };
|
|
67
|
+
if (!prize) return { error: "Prize is required." };
|
|
63
68
|
|
|
64
69
|
if (type === "reaction" && !emoji) return { error: "Emoji is not required." };
|
|
65
70
|
|
|
@@ -91,11 +96,20 @@ async function Gstart({
|
|
|
91
96
|
if (Sthumbnail) embed.setThumbnail(Sthumbnail);
|
|
92
97
|
}
|
|
93
98
|
|
|
99
|
+
let requirementsText = "";
|
|
100
|
+
if (requiredRoles.length > 0) {
|
|
101
|
+
requirementsText = `\n- Required Roles: ${requiredRoles
|
|
102
|
+
.map((r) => `<@&${r}>`)
|
|
103
|
+
.join(", ")}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
94
106
|
embed.addFields({
|
|
95
107
|
name: "🎉 Giveaway",
|
|
96
|
-
value: `-
|
|
108
|
+
value: `- Prize: ${prize}\n- Winner(s) : ${winnerCount}\n- Time : <t:${Math.floor(
|
|
97
109
|
endTime / 1000
|
|
98
|
-
)}:R>\n- Hosted By : ${
|
|
110
|
+
)}:R>\n- Hosted By : ${
|
|
111
|
+
context.user ? context.user : context.author
|
|
112
|
+
}${requirementsText}`,
|
|
99
113
|
inline: true,
|
|
100
114
|
});
|
|
101
115
|
|
|
@@ -124,7 +138,7 @@ async function Gstart({
|
|
|
124
138
|
|
|
125
139
|
end_embed.addFields({
|
|
126
140
|
name: "🎉 Giveaway",
|
|
127
|
-
value: `-
|
|
141
|
+
value: `- Prize: ${prize}\n- Winner(s): ${winnerCount}\n- Time : <t:${Math.floor(
|
|
128
142
|
endTime / 1000
|
|
129
143
|
)}:R>\n- Hosted By : ${context.user ? context.user : context.author}`,
|
|
130
144
|
inline: true,
|
|
@@ -136,6 +150,7 @@ async function Gstart({
|
|
|
136
150
|
messageId: message.id,
|
|
137
151
|
hoster: context.user ? context.user.id : context.author.id,
|
|
138
152
|
endTime: endTime,
|
|
153
|
+
prize: prize,
|
|
139
154
|
endType: false,
|
|
140
155
|
paused: false,
|
|
141
156
|
pausedTime: [],
|
|
@@ -145,6 +160,7 @@ async function Gstart({
|
|
|
145
160
|
reaction: type,
|
|
146
161
|
endEmbed: end_embed.toJSON(),
|
|
147
162
|
users: [],
|
|
163
|
+
requiredRoles: requiredRoles,
|
|
148
164
|
});
|
|
149
165
|
|
|
150
166
|
if (type === "reaction") message.react(emoji);
|
|
@@ -293,6 +309,7 @@ async function Glist(type) {
|
|
|
293
309
|
|
|
294
310
|
const list = giveaways.map((g) => {
|
|
295
311
|
return {
|
|
312
|
+
prize: g.prize,
|
|
296
313
|
messageId: g.messageId,
|
|
297
314
|
guildId: g.guildId,
|
|
298
315
|
channelId: g.channelId,
|
|
@@ -328,7 +345,7 @@ async function Gpause(client, messageId) {
|
|
|
328
345
|
|
|
329
346
|
message.embeds[0].data.fields.find(
|
|
330
347
|
(f) => f.name === "🎉 Giveaway"
|
|
331
|
-
).value = `-
|
|
348
|
+
).value = `- Winner(s): ${g.winnerCount}\n- Time : Pause ⏸️\n- Hosted By : <@${g.hoster}>`;
|
|
332
349
|
|
|
333
350
|
await message.edit({ embeds: [message.embeds[0]] });
|
|
334
351
|
await giveaway.findOneAndUpdate(
|
|
@@ -343,7 +360,7 @@ async function Gresume(client, messageId) {
|
|
|
343
360
|
const g = await giveaway.findOne({ messageId });
|
|
344
361
|
if (!g) return { error: "❌ Giveaway Data Not Found" };
|
|
345
362
|
|
|
346
|
-
if (g.paused ===
|
|
363
|
+
if (g.paused === false) return { error: "❌ Giveaway Already Paused" };
|
|
347
364
|
if (g.ended === true) return { error: "❌ Giveaway Already Ended" };
|
|
348
365
|
|
|
349
366
|
const guild = await client.guilds.cache.get(g.guildId);
|
|
@@ -356,7 +373,7 @@ async function Gresume(client, messageId) {
|
|
|
356
373
|
|
|
357
374
|
message.embeds[0].data.fields.find(
|
|
358
375
|
(f) => f.name === "🎉 Giveaway"
|
|
359
|
-
).value = `-
|
|
376
|
+
).value = `- Winner(s): ${g.winnerCount}\n- Time : <t:${Math.floor(
|
|
360
377
|
time / 1000
|
|
361
378
|
)}:R>\n- Hosted By : <@${g.hoster}>`;
|
|
362
379
|
|
|
@@ -386,13 +403,32 @@ async function Gdelete(messageId) {
|
|
|
386
403
|
}
|
|
387
404
|
|
|
388
405
|
////////////////////////////////////* Giveaway Add User
|
|
389
|
-
async function GaddUser(messageId, userId) {
|
|
406
|
+
async function GaddUser(messageId, userId, guild) {
|
|
390
407
|
const g = await giveaway.findOne({ messageId });
|
|
391
408
|
if (!g) return { error: "❌ Giveaway Data Not Found" };
|
|
392
409
|
|
|
410
|
+
if (g.ended) return { error: "❌ Giveaway Already Ended" };
|
|
411
|
+
|
|
393
412
|
const all_users = g.users || [];
|
|
394
413
|
if (all_users.includes(userId)) return { error: "❌ User Already Joined" };
|
|
395
414
|
|
|
415
|
+
if (g.requiredRoles && g.requiredRoles.length > 0) {
|
|
416
|
+
if (!guild) return { error: "❌ Guild object is required to check roles." };
|
|
417
|
+
const member = await guild.members.fetch(userId).catch(() => null);
|
|
418
|
+
if (!member) return { error: "❌ Member not found in guild." };
|
|
419
|
+
|
|
420
|
+
const hasRole = g.requiredRoles.some((roleId) =>
|
|
421
|
+
member.roles.cache.has(roleId)
|
|
422
|
+
);
|
|
423
|
+
if (!hasRole) {
|
|
424
|
+
return {
|
|
425
|
+
error: `❌ You need one of the following roles to join: ${g.requiredRoles
|
|
426
|
+
.map((r) => `<@&${r}>`)
|
|
427
|
+
.join(", ")}`,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
396
432
|
if (!all_users.includes(userId)) {
|
|
397
433
|
all_users.push(userId);
|
|
398
434
|
await giveaway.findOneAndUpdate({ messageId }, { users: all_users });
|
|
@@ -434,7 +470,7 @@ async function GaddTime(messageId, client, time) {
|
|
|
434
470
|
|
|
435
471
|
message.embeds[0].data.fields.find(
|
|
436
472
|
(f) => f.name === "🎉 Giveaway"
|
|
437
|
-
).value = `-
|
|
473
|
+
).value = `- Winner(s): ${g.winnerCount}\n- Time : <t:${Math.floor(
|
|
438
474
|
finel_time / 1000
|
|
439
475
|
)}:R>\n- Hosted By : <@${g.hoster}>`;
|
|
440
476
|
|
|
@@ -466,7 +502,7 @@ async function GremoveTime(messageId, client, time) {
|
|
|
466
502
|
|
|
467
503
|
message.embeds[0].data.fields.find(
|
|
468
504
|
(f) => f.name === "🎉 Giveaway"
|
|
469
|
-
).value = `-
|
|
505
|
+
).value = `- Winner(s): ${g.winnerCount}\n- Time : <t:${Math.floor(
|
|
470
506
|
finel_time / 1000
|
|
471
507
|
)}:R>\n- Hosted By : <@${g.hoster}>`;
|
|
472
508
|
|
|
@@ -487,6 +523,7 @@ async function Gdata(messageId) {
|
|
|
487
523
|
}
|
|
488
524
|
|
|
489
525
|
module.exports = {
|
|
526
|
+
giveaway,
|
|
490
527
|
Gstart,
|
|
491
528
|
Gcheck,
|
|
492
529
|
Greroll,
|
|
File without changes
|
package/handler/starter.js
CHANGED
|
@@ -6,6 +6,7 @@ const {
|
|
|
6
6
|
cmd_log,
|
|
7
7
|
update,
|
|
8
8
|
} = require("./helper");
|
|
9
|
+
const { dashboard } = require("../function/dash");
|
|
9
10
|
let termenal = {
|
|
10
11
|
prefix: 0,
|
|
11
12
|
slash: 0,
|
|
@@ -150,7 +151,13 @@ async function starter(client, options) {
|
|
|
150
151
|
const events = options.events || null;
|
|
151
152
|
const anticrash = options.anticrash || null;
|
|
152
153
|
const terminal = options.terminal || null;
|
|
154
|
+
const dashboard = options.dashboard || null;
|
|
153
155
|
|
|
156
|
+
//////////////////////////////////////////? Dashboard Loader ////////////////////////////////////////////////////////
|
|
157
|
+
if (dashboard) {
|
|
158
|
+
await dashboard(client, dashboard);
|
|
159
|
+
}
|
|
160
|
+
//////////////////////////////////////////! terminal info !////////////////////////////////////////////////////////
|
|
154
161
|
if (terminal) {
|
|
155
162
|
console.log("🚀 Bot is starting...");
|
|
156
163
|
}
|
|
@@ -260,12 +267,13 @@ async function starter(client, options) {
|
|
|
260
267
|
if (command.guildOnly && !message.guild) return;
|
|
261
268
|
if (command.dmOnly && message.guild) return;
|
|
262
269
|
if (command.devOnly && message.author.id !== client.owner.id) return;
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
270
|
+
if (command.ownerOnly) {
|
|
271
|
+
if (
|
|
272
|
+
message.guild &&
|
|
273
|
+
message.author.id !== (await message.guild.fetchOwner()).id
|
|
274
|
+
)
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
269
277
|
|
|
270
278
|
if (
|
|
271
279
|
command.Permissions &&
|
|
@@ -274,11 +282,13 @@ async function starter(client, options) {
|
|
|
274
282
|
)
|
|
275
283
|
return;
|
|
276
284
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
285
|
+
if (command.Blacklist) {
|
|
286
|
+
const userBlacklisted = await isBlacklisted(
|
|
287
|
+
message.guild ? message.guild.id : null,
|
|
288
|
+
"user",
|
|
289
|
+
message.author.id
|
|
290
|
+
);
|
|
291
|
+
if (userBlacklisted) {
|
|
282
292
|
return message.reply({
|
|
283
293
|
content: "`❌` You are blacklisted from using bot commands.",
|
|
284
294
|
flags: 64,
|
|
@@ -287,23 +297,33 @@ async function starter(client, options) {
|
|
|
287
297
|
|
|
288
298
|
let roleBlacklist = null;
|
|
289
299
|
for (const [id, role] of message.member.roles.cache) {
|
|
290
|
-
const isBlocked = await isBlacklisted(
|
|
300
|
+
const isBlocked = await isBlacklisted(
|
|
301
|
+
message.guild ? message.guild.id : null,
|
|
302
|
+
"role",
|
|
303
|
+
id
|
|
304
|
+
);
|
|
291
305
|
if (isBlocked) {
|
|
292
306
|
roleBlacklist = isBlocked;
|
|
293
307
|
break;
|
|
294
308
|
}
|
|
295
309
|
}
|
|
296
|
-
|
|
297
|
-
if(roleBlacklist) {
|
|
310
|
+
|
|
311
|
+
if (roleBlacklist) {
|
|
298
312
|
const role = message.guild.roles.cache.get(roleBlacklist.id);
|
|
299
313
|
return message.reply({
|
|
300
|
-
content: `\`❌\` The role **${
|
|
314
|
+
content: `\`❌\` The role **${
|
|
315
|
+
role ? role.name : "Unknown"
|
|
316
|
+
}** is blacklisted from using bot commands.`,
|
|
301
317
|
flags: 64,
|
|
302
318
|
});
|
|
303
319
|
}
|
|
304
320
|
|
|
305
|
-
const channelBlacklist = await isBlacklisted(
|
|
306
|
-
|
|
321
|
+
const channelBlacklist = await isBlacklisted(
|
|
322
|
+
message.guild ? message.guild.id : null,
|
|
323
|
+
"channel",
|
|
324
|
+
message.channel.id
|
|
325
|
+
);
|
|
326
|
+
if (channelBlacklist) {
|
|
307
327
|
return message.reply({
|
|
308
328
|
content: "`❌` This channel is blacklisted from using bot commands.",
|
|
309
329
|
flags: 64,
|
|
@@ -346,15 +366,15 @@ async function starter(client, options) {
|
|
|
346
366
|
if (cooldown.type) return interaction.reply(cooldown.text);
|
|
347
367
|
}
|
|
348
368
|
|
|
349
|
-
if (
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
369
|
+
if (command.ownerOnly) {
|
|
370
|
+
if (
|
|
371
|
+
interaction.guild &&
|
|
372
|
+
interaction.user.id !== (await interaction.guild.fetchOwner()).id
|
|
373
|
+
)
|
|
374
|
+
return interaction.reply({
|
|
375
|
+
content: "`❌` This command is restricted to the server owner only!",
|
|
376
|
+
flags: 64,
|
|
377
|
+
});
|
|
358
378
|
}
|
|
359
379
|
|
|
360
380
|
if (command.devOnly && interaction.user.id !== client.owner.id) {
|
|
@@ -386,15 +406,19 @@ async function starter(client, options) {
|
|
|
386
406
|
)
|
|
387
407
|
) {
|
|
388
408
|
return interaction.reply({
|
|
389
|
-
content:
|
|
409
|
+
content:
|
|
410
|
+
"`❌` You do not have enough permissions to execute this command.",
|
|
390
411
|
flags: 64,
|
|
391
412
|
});
|
|
392
413
|
}
|
|
393
414
|
|
|
394
|
-
if(command.Blacklist) {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
415
|
+
if (command.Blacklist) {
|
|
416
|
+
const userBlacklisted = await isBlacklisted(
|
|
417
|
+
interaction.guild ? interaction.guild.id : null,
|
|
418
|
+
"user",
|
|
419
|
+
interaction.user.id
|
|
420
|
+
);
|
|
421
|
+
if (userBlacklisted) {
|
|
398
422
|
return interaction.reply({
|
|
399
423
|
content: "`❌` You are blacklisted from using bot commands.",
|
|
400
424
|
flags: 64,
|
|
@@ -403,23 +427,33 @@ async function starter(client, options) {
|
|
|
403
427
|
|
|
404
428
|
let roleBlacklist = null;
|
|
405
429
|
for (const [id, role] of interaction.member.roles.cache) {
|
|
406
|
-
const isBlocked = await isBlacklisted(
|
|
430
|
+
const isBlocked = await isBlacklisted(
|
|
431
|
+
interaction.guild ? interaction.guild.id : null,
|
|
432
|
+
"role",
|
|
433
|
+
id
|
|
434
|
+
);
|
|
407
435
|
if (isBlocked) {
|
|
408
436
|
roleBlacklist = isBlocked;
|
|
409
437
|
break;
|
|
410
438
|
}
|
|
411
439
|
}
|
|
412
440
|
|
|
413
|
-
if(roleBlacklist) {
|
|
441
|
+
if (roleBlacklist) {
|
|
414
442
|
const role = interaction.guild.roles.cache.get(roleBlacklist.id);
|
|
415
443
|
return interaction.reply({
|
|
416
|
-
content: `\`❌\` The role **${
|
|
444
|
+
content: `\`❌\` The role **${
|
|
445
|
+
role ? role.name : "Unknown"
|
|
446
|
+
}** is blacklisted from using bot commands.`,
|
|
417
447
|
flags: 64,
|
|
418
448
|
});
|
|
419
449
|
}
|
|
420
450
|
|
|
421
|
-
const channelBlacklist = await isBlacklisted(
|
|
422
|
-
|
|
451
|
+
const channelBlacklist = await isBlacklisted(
|
|
452
|
+
interaction.guild ? interaction.guild.id : null,
|
|
453
|
+
"channel",
|
|
454
|
+
interaction.channel.id
|
|
455
|
+
);
|
|
456
|
+
if (channelBlacklist) {
|
|
423
457
|
return interaction.reply({
|
|
424
458
|
content: "`❌` This channel is blacklisted from using bot commands.",
|
|
425
459
|
flags: 64,
|
|
@@ -488,7 +522,7 @@ const {
|
|
|
488
522
|
isBlacklisted,
|
|
489
523
|
addToBlacklist,
|
|
490
524
|
removeFromBlacklist,
|
|
491
|
-
getBlacklist
|
|
525
|
+
getBlacklist,
|
|
492
526
|
} = require("../function/blacklist");
|
|
493
527
|
const {
|
|
494
528
|
giveaway,
|
|
@@ -510,6 +544,7 @@ module.exports = {
|
|
|
510
544
|
Level,
|
|
511
545
|
giveaway,
|
|
512
546
|
starter,
|
|
547
|
+
dashboard,
|
|
513
548
|
reload,
|
|
514
549
|
log,
|
|
515
550
|
Wait,
|
|
@@ -535,5 +570,5 @@ module.exports = {
|
|
|
535
570
|
isBlacklisted,
|
|
536
571
|
addToBlacklist,
|
|
537
572
|
removeFromBlacklist,
|
|
538
|
-
getBlacklist
|
|
573
|
+
getBlacklist,
|
|
539
574
|
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "djs-builder",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"note": "🎉 Package Update! 🥏\n\n-
|
|
5
|
-
"description": "🎉
|
|
3
|
+
"version": "0.7.1",
|
|
4
|
+
"note": "🎉 Package Update! 🥏\n\n- 🌐 NEW: Dashboard System - Modern web-based control panel!\n • Discord OAuth2 Login\n • Server Management\n • Level System Control\n • Giveaway Management\n • Blacklist Management\n- � Documentation: Full Dashboard guide added to README.\n- ✨ Standalone Usage: Use Dashboard with or without Starter!\n\n🔗 Learn more on [NPM](https://www.npmjs.com/package/djs-builder)",
|
|
5
|
+
"description": "🎉 NEW: Dashboard System - Web Control Panel! 🌐",
|
|
6
6
|
"main": "handler/starter.js",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"axios": "^1.11.0",
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
"cli-table3": "^0.6.5",
|
|
11
11
|
"discord-inviter": "^0.9.3",
|
|
12
12
|
"discord.js": "^14.21.0",
|
|
13
|
-
"mongoose": "^8.18.0"
|
|
13
|
+
"mongoose": "^8.18.0",
|
|
14
|
+
"express": "^4.18.2",
|
|
15
|
+
"express-session": "^1.17.3",
|
|
16
|
+
"passport": "^0.7.0",
|
|
17
|
+
"passport-discord": "^0.1.4",
|
|
18
|
+
"ejs": "^3.1.9"
|
|
14
19
|
}
|
|
15
20
|
}
|
package/views/404.ejs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="ar" dir="rtl">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>404 - الصفحة غير موجودة</title>
|
|
7
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
8
|
+
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
|
|
9
|
+
<style>
|
|
10
|
+
:root { --bg-primary: #0f0f0f; --bg-secondary: #1a1a1a; --bg-card: #242424; --accent: #5865F2; --accent-hover: #4752C4; --text-primary: #fff; --text-secondary: #b9bbbe; --text-muted: #72767d; --border: #2f2f2f; --radius: 12px; --radius-sm: 8px; }
|
|
11
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
12
|
+
body { font-family: 'Inter', sans-serif; background: var(--bg-primary); color: var(--text-primary); min-height: 100vh; display: flex; align-items: center; justify-content: center; }
|
|
13
|
+
.error-container { text-align: center; padding: 40px; max-width: 500px; }
|
|
14
|
+
.error-code { font-size: 120px; font-weight: 700; color: var(--accent); line-height: 1; margin-bottom: 16px; text-shadow: 0 0 60px rgba(88, 101, 242, 0.3); }
|
|
15
|
+
.error-icon { font-size: 64px; color: var(--text-muted); margin-bottom: 24px; }
|
|
16
|
+
.error-title { font-size: 28px; font-weight: 600; margin-bottom: 12px; }
|
|
17
|
+
.error-desc { font-size: 16px; color: var(--text-secondary); margin-bottom: 32px; line-height: 1.6; }
|
|
18
|
+
.btn { display: inline-flex; align-items: center; gap: 8px; padding: 12px 24px; border-radius: var(--radius-sm); font-weight: 500; font-size: 15px; transition: all 0.2s ease; text-decoration: none; }
|
|
19
|
+
.btn-primary { background: var(--accent); color: white; }
|
|
20
|
+
.btn-primary:hover { background: var(--accent-hover); transform: translateY(-2px); }
|
|
21
|
+
.btn-secondary { background: var(--bg-card); color: var(--text-primary); border: 1px solid var(--border); margin-right: 12px; }
|
|
22
|
+
.btn-secondary:hover { background: var(--bg-secondary); }
|
|
23
|
+
.buttons { display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; }
|
|
24
|
+
@keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
|
|
25
|
+
.error-icon { animation: float 3s ease-in-out infinite; }
|
|
26
|
+
</style>
|
|
27
|
+
</head>
|
|
28
|
+
<body>
|
|
29
|
+
<div class="error-container">
|
|
30
|
+
<div class="error-code">404</div>
|
|
31
|
+
<i class="ri-compass-discover-line error-icon"></i>
|
|
32
|
+
<h1 class="error-title">الصفحة غير موجودة</h1>
|
|
33
|
+
<p class="error-desc">عذراً، الصفحة التي تبحث عنها غير موجودة أو تم نقلها. تأكد من صحة الرابط أو عد للصفحة الرئيسية.</p>
|
|
34
|
+
<div class="buttons">
|
|
35
|
+
<a href="/" class="btn btn-primary"><i class="ri-home-line"></i> الصفحة الرئيسية</a>
|
|
36
|
+
<a href="/dashboard" class="btn btn-secondary"><i class="ri-dashboard-line"></i> لوحة التحكم</a>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</body>
|
|
40
|
+
</html>
|