This commit is contained in:
Tutur33
2023-12-06 22:34:07 +01:00
parent 62974abf9e
commit 977386f605
57 changed files with 1219 additions and 2 deletions
+1 -1
View File
@@ -5,4 +5,4 @@ APP_KEY=1Ad59qH3RodlK8Py5hr2WA2VeNUXEqmL
DRIVE_DISK=local
SESSION_DRIVER=cookie
CACHE_VIEWS=false
DB_CONNECTION=sqlite
DB_CONNECTION=sqlite
-1
View File
@@ -1,5 +1,4 @@
node_modules
build
coverage
.vscode
.DS_STORE
@@ -0,0 +1,44 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const User_1 = __importDefault(global[Symbol.for('ioc.use')]("App/Models/User"));
const CreateUserValidator_1 = __importDefault(global[Symbol.for('ioc.use')]("App/Validators/CreateUserValidator"));
class AuthController {
async login({ view }) {
return view.render('auth/login');
}
async doLogin({ request, auth, response, session }) {
const email = request.input('email');
const password = request.input('password');
try {
await auth.use('web').attempt(email, password);
response.redirect().toRoute('home');
}
catch {
session.flash({ error: 'Identifiants incorrects' });
response.redirect().toRoute('login');
}
}
async signup({ view }) {
return view.render('auth/signup');
}
async doSignup({ request, response, auth }) {
const playload = await request.validate(CreateUserValidator_1.default);
await User_1.default.create(playload);
try {
await auth.use('web').attempt(playload.email, playload.password);
response.redirect().toRoute('home');
}
catch {
response.redirect().toRoute('login');
}
}
async logout({ auth, response }) {
await auth.logout();
return response.redirect().back();
}
}
exports.default = AuthController;
//# sourceMappingURL=AuthController.js.map
@@ -0,0 +1 @@
{"version":3,"file":"AuthController.js","sourceRoot":"","sources":["../../../../app/Controllers/Http/AuthController.ts"],"names":[],"mappings":";;;;;AACA,iFAAmC;AACnC,mHAAqE;AAGrE,MAAqB,cAAc;IAEjC,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,EAAuB;QACvC,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IAClC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAuB;QACrE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QAE1C,IAAI;YACF,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;YAC9C,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;SACpC;QAAC,MAAM;YACN,OAAO,CAAC,KAAK,CAAC,EAAC,KAAK,EAAE,yBAAyB,EAAC,CAAC,CAAA;YACjD,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;SACrC;IACH,CAAC;IAGD,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAuB;QACxC,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;IACnC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAuB;QAC7D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,6BAAmB,CAAC,CAAA;QAC5D,MAAM,cAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QAC3B,IAAI;YACF,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAA;YAChE,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;SACpC;QAAC,MAAM;YACN,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;SACrC;IACH,CAAC;IAGD,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAsB;QACjD,MAAM,IAAI,CAAC,MAAM,EAAE,CAAA;QACnB,OAAO,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAA;IACpC,CAAC;CACD;AAxCD,iCAwCC"}
@@ -0,0 +1,36 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ModifPseudoValidator_1 = __importDefault(global[Symbol.for('ioc.use')]("App/Validators/ModifPseudoValidator"));
const ModifEmailValidator_1 = __importDefault(global[Symbol.for('ioc.use')]("App/Validators/ModifEmailValidator"));
class CompteController {
async index({ view }) {
return view.render('compte');
}
async modifpseudo({ request, auth, session, response }) {
const user = auth.user;
await request.validate(ModifPseudoValidator_1.default);
user.pseudo = request.input('pseudo');
await user.save();
session.flash({ success: "Username updated successfully" });
response.redirect().back();
}
async modifemail({ request, auth, session, response }) {
const user = auth.user;
await request.validate(ModifEmailValidator_1.default);
user.email = request.input('email');
await user.save();
session.flash({ success: "Email updated successfully" });
response.redirect().back();
}
async delete({ auth, response }) {
const user = auth.user;
await user.delete();
await auth.logout();
response.redirect().toRoute('home');
}
}
exports.default = CompteController;
//# sourceMappingURL=CompteController.js.map
@@ -0,0 +1 @@
{"version":3,"file":"CompteController.js","sourceRoot":"","sources":["../../../../app/Controllers/Http/CompteController.ts"],"names":[],"mappings":";;;;;AACA,qHAAsE;AACtE,mHAAoE;AAEpE,MAAqB,gBAAgB;IAEnC,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,EAAuB;QACvC,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAuB;QACzE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QAEtB,MAAM,OAAO,CAAC,QAAQ,CAAC,8BAAoB,CAAC,CAAA;QAE5C,IAAK,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QACtC,MAAM,IAAK,CAAC,IAAI,EAAE,CAAA;QAClB,OAAO,CAAC,KAAK,CAAC,EAAC,OAAO,EAAE,+BAA+B,EAAC,CAAC,CAAA;QACzD,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAuB;QACxE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QAEtB,MAAM,OAAO,CAAC,QAAQ,CAAC,6BAAmB,CAAC,CAAA;QAE3C,IAAK,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACpC,MAAM,IAAK,CAAC,IAAI,EAAE,CAAA;QAClB,OAAO,CAAC,KAAK,CAAC,EAAC,OAAO,EAAE,4BAA4B,EAAC,CAAC,CAAA;QACtD,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAuB;QAClD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QACtB,MAAM,IAAK,CAAC,MAAM,EAAE,CAAA;QACpB,MAAM,IAAI,CAAC,MAAM,EAAE,CAAA;QACnB,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IACrC,CAAC;CAEF;AAnCD,mCAmCC"}
+26
View File
@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const standalone_1 = require("@adonisjs/auth/build/standalone");
class AuthMiddleware {
constructor() {
this.redirectTo = 'auth/login';
}
async authenticate(auth, guards) {
let guardLastAttempted;
for (let guard of guards) {
guardLastAttempted = guard;
if (await auth.use(guard).check()) {
auth.defaultGuard = guard;
return true;
}
}
throw new standalone_1.AuthenticationException('Unauthorized access', 'E_UNAUTHORIZED_ACCESS', guardLastAttempted, this.redirectTo);
}
async handle({ auth }, next, customGuards) {
const guards = customGuards.length ? customGuards : [auth.name];
await this.authenticate(auth, guards);
await next();
}
}
exports.default = AuthMiddleware;
//# sourceMappingURL=Auth.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Auth.js","sourceRoot":"","sources":["../../../app/Middleware/Auth.ts"],"names":[],"mappings":";;AAAA,gEAAyE;AAWzE,MAAqB,cAAc;IAAnC;QAIY,eAAU,GAAG,YAAY,CAAA;IA4DrC,CAAC;IAlDW,KAAK,CAAC,YAAY,CAAC,IAAiC,EAAE,MAA4B;QAO1F,IAAI,kBAAsC,CAAA;QAE1C,KAAK,IAAI,KAAK,IAAI,MAAM,EAAE;YACxB,kBAAkB,GAAG,KAAK,CAAA;YAE1B,IAAI,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBAMjC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAA;gBACzB,OAAO,IAAI,CAAA;aACZ;SACF;QAKD,MAAM,IAAI,oCAAuB,CAC/B,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,EAClB,IAAI,CAAC,UAAU,CAChB,CAAA;IACH,CAAC;IAKM,KAAK,CAAC,MAAM,CACjB,EAAE,IAAI,EAAuB,EAC7B,IAAyB,EACzB,YAAkC;QAMlC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC/D,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACrC,MAAM,IAAI,EAAE,CAAA;IACd,CAAC;CACF;AAhED,iCAgEC"}
+10
View File
@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class SilentAuthMiddleware {
async handle({ auth }, next) {
await auth.check();
await next();
}
}
exports.default = SilentAuthMiddleware;
//# sourceMappingURL=SilentAuth.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"SilentAuth.js","sourceRoot":"","sources":["../../../app/Middleware/SilentAuth.ts"],"names":[],"mappings":";;AAQA,MAAqB,oBAAoB;IAIhC,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAuB,EAAE,IAAyB;QAK1E,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;QAClB,MAAM,IAAI,EAAE,CAAA;IACd,CAAC;CACF;AAZD,uCAYC"}
+76
View File
@@ -0,0 +1,76 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const luxon_1 = require("luxon");
const Hash_1 = __importDefault(global[Symbol.for('ioc.use')]("Adonis/Core/Hash"));
const Orm_1 = global[Symbol.for('ioc.use')]("Adonis/Lucid/Orm");
const ResponsiveAttachment_1 = global[Symbol.for('ioc.use')]("Adonis/Addons/ResponsiveAttachment");
class User extends Orm_1.BaseModel {
get avatarUrl() {
if (this.avatar) {
return '/uploads/' + this.avatar.breakpoints.small.name;
}
return 'https://source.boringavatars.com/beam/40/${this.email}?colors=001449,012677,005BC5,00B4FC,17F9FF';
}
static async hashPassword(user) {
if (user.$dirty.password) {
user.password = await Hash_1.default.make(user.password);
}
}
}
__decorate([
(0, Orm_1.column)({ isPrimary: true }),
__metadata("design:type", Number)
], User.prototype, "id", void 0);
__decorate([
(0, Orm_1.column)(),
__metadata("design:type", String)
], User.prototype, "pseudo", void 0);
__decorate([
(0, Orm_1.column)(),
__metadata("design:type", String)
], User.prototype, "email", void 0);
__decorate([
(0, Orm_1.column)({ serializeAs: null }),
__metadata("design:type", String)
], User.prototype, "password", void 0);
__decorate([
(0, Orm_1.column)(),
__metadata("design:type", Object)
], User.prototype, "rememberMeToken", void 0);
__decorate([
Orm_1.column.dateTime({ autoCreate: true }),
__metadata("design:type", luxon_1.DateTime)
], User.prototype, "createdAt", void 0);
__decorate([
Orm_1.column.dateTime({ autoCreate: true, autoUpdate: true }),
__metadata("design:type", luxon_1.DateTime)
], User.prototype, "updatedAt", void 0);
__decorate([
(0, ResponsiveAttachment_1.responsiveAttachment)({ preComputeUrls: true }),
__metadata("design:type", Object)
], User.prototype, "avatar", void 0);
__decorate([
(0, Orm_1.computed)(),
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], User.prototype, "avatarUrl", null);
__decorate([
(0, Orm_1.beforeSave)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [User]),
__metadata("design:returntype", Promise)
], User, "hashPassword", null);
exports.default = User;
//# sourceMappingURL=User.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"User.js","sourceRoot":"","sources":["../../../app/Models/User.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,iCAAgC;AAChC,kFAAwC;AACxC,gEAA+E;AAC/E,mGAA4G;AAE5G,MAAqB,IAAK,SAAQ,eAAS;IA0BzC,IAAW,SAAS;QAClB,IAAI,IAAI,CAAC,MAAM,EAAE;YACf,OAAO,WAAW,GAAC,IAAI,CAAC,MAAM,CAAC,WAAY,CAAC,KAAK,CAAC,IAAI,CAAA;SACvD;QACD,OAAO,kGAAkG,CAAA;IAC3G,CAAC;IAGM,MAAM,CAAC,KAAK,CAAC,YAAY,CAAE,IAAU;QAC1C,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;YACxB,IAAI,CAAC,QAAQ,GAAG,MAAM,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;SAC/C;IACH,CAAC;CACF;AArCC;IADC,IAAA,YAAM,EAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;;gCACX;AAGjB;IADC,IAAA,YAAM,GAAE;;oCACY;AAGrB;IADC,IAAA,YAAM,GAAE;;mCACW;AAGpB;IADC,IAAA,YAAM,EAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;;sCACP;AAGvB;IADC,IAAA,YAAM,GAAE;;6CAC4B;AAGrC;IADC,YAAM,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;8BACpB,gBAAQ;uCAAA;AAG1B;IADC,YAAM,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;8BACtC,gBAAQ;uCAAA;AAG1B;IADC,IAAA,2CAAoB,EAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;;oCACJ;AAG3C;IADC,IAAA,cAAQ,GAAE;;;qCAMV;AAGD;IADC,IAAA,gBAAU,GAAE;;qCAC2B,IAAI;;8BAI3C;AAtCH,uBAuCC"}
@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Validator_1 = global[Symbol.for('ioc.use')]("Adonis/Core/Validator");
class CreateUserValidator {
constructor(ctx) {
this.ctx = ctx;
this.schema = Validator_1.schema.create({
pseudo: Validator_1.schema.string(),
email: Validator_1.schema.string({}, [Validator_1.rules.email(), Validator_1.rules.unique({ table: 'users', column: 'email' })]),
password: Validator_1.schema.string({}, [Validator_1.rules.minLength(4), Validator_1.rules.confirmed()])
});
this.messages = {
required: 'The {{ field }} is required to create a new account',
'email.email': 'Vous devez saisir un email dans le champ email',
'email.unique': 'Email is already in use',
'password.minLength': 'The password must be at least 4 characters long'
};
}
}
exports.default = CreateUserValidator;
//# sourceMappingURL=CreateUserValidator.js.map
@@ -0,0 +1 @@
{"version":3,"file":"CreateUserValidator.js","sourceRoot":"","sources":["../../../app/Validators/CreateUserValidator.ts"],"names":[],"mappings":";;AAAA,2EAA0E;AAG1E,MAAqB,mBAAmB;IACtC,YAAsB,GAAwB;QAAxB,QAAG,GAAH,GAAG,CAAqB;QAqBvC,WAAM,GAAG,kBAAM,CAAC,MAAM,CAAC;YAC5B,MAAM,EAAE,kBAAM,CAAC,MAAM,EAAE;YACvB,KAAK,EAAE,kBAAM,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,iBAAK,CAAC,KAAK,EAAE,EAAE,iBAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAE,CAAC;YAC7F,QAAQ,EAAE,kBAAM,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,iBAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,iBAAK,CAAC,SAAS,EAAE,CAAE,CAAC;SACtE,CAAC,CAAA;QAaK,aAAQ,GAAmB;YAChC,QAAQ,EAAE,qDAAqD;YAC/D,aAAa,EAAE,gDAAgD;YAC/D,cAAc,EAAE,yBAAyB;YACzC,oBAAoB,EAAE,iDAAiD;SACxE,CAAA;IA3CgD,CAAC;CA4CnD;AA7CD,sCA6CC"}
@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Validator_1 = global[Symbol.for('ioc.use')]("Adonis/Core/Validator");
class ModifEmailValidator {
constructor(ctx) {
this.ctx = ctx;
this.schema = Validator_1.schema.create({
email: Validator_1.schema.string({}, [Validator_1.rules.email(), Validator_1.rules.unique({ table: 'users', column: 'email' })])
});
this.messages = {
required: 'The {{ field }} is required to modifie email',
'email.email': 'You must enter an email in the email field',
'email.unique': 'Email is already in use'
};
}
}
exports.default = ModifEmailValidator;
//# sourceMappingURL=ModifEmailValidator.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ModifEmailValidator.js","sourceRoot":"","sources":["../../../app/Validators/ModifEmailValidator.ts"],"names":[],"mappings":";;AAAA,2EAA0E;AAG1E,MAAqB,mBAAmB;IACtC,YAAsB,GAAwB;QAAxB,QAAG,GAAH,GAAG,CAAqB;QAqBvC,WAAM,GAAG,kBAAM,CAAC,MAAM,CAAC;YAC5B,KAAK,EAAE,kBAAM,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,iBAAK,CAAC,KAAK,EAAE,EAAE,iBAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAE,CAAC;SAC9F,CAAC,CAAA;QAaK,aAAQ,GAAmB;YAChC,QAAQ,EAAE,8CAA8C;YACxD,aAAa,EAAE,4CAA4C;YAC3D,cAAc,EAAE,yBAAyB;SAC1C,CAAA;IAxCgD,CAAC;CAyCnD;AA1CD,sCA0CC"}
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Validator_1 = global[Symbol.for('ioc.use')]("Adonis/Core/Validator");
class ModifPseudoValidator {
constructor(ctx) {
this.ctx = ctx;
this.schema = Validator_1.schema.create({
pseudo: Validator_1.schema.string({}, [Validator_1.rules.minLength(3)])
});
this.messages = {
required: 'The {{ field }} is required to modifie pseudo',
'pseudo.minLength': 'The pseudo must be at least 3 characters long'
};
}
}
exports.default = ModifPseudoValidator;
//# sourceMappingURL=ModifPseudoValidator.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ModifPseudoValidator.js","sourceRoot":"","sources":["../../../app/Validators/ModifPseudoValidator.ts"],"names":[],"mappings":";;AAAA,2EAA0E;AAG1E,MAAqB,oBAAoB;IACvC,YAAsB,GAAwB;QAAxB,QAAG,GAAH,GAAG,CAAqB;QAqBvC,WAAM,GAAG,kBAAM,CAAC,MAAM,CAAC;YAC5B,MAAM,EAAE,kBAAM,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,iBAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;SAChD,CAAC,CAAA;QAaK,aAAQ,GAAmB;YAChC,QAAQ,EAAE,+CAA+C;YACzD,kBAAkB,EAAE,+CAA+C;SACpE,CAAA;IAvCgD,CAAC;CAwCnD;AAzCD,uCAyCC"}
+41
View File
@@ -0,0 +1,41 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const authConfig = {
guard: 'web',
guards: {
web: {
driver: 'session',
provider: {
driver: 'lucid',
identifierKey: 'id',
uids: ['email'],
model: () => Promise.resolve().then(() => __importStar(global[Symbol.for('ioc.use')]('App/Models/User'))),
},
},
},
};
exports.default = authConfig;
//# sourceMappingURL=auth.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../config/auth.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAkBA,MAAM,UAAU,GAAe;IAC7B,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE;QAWN,GAAG,EAAE;YACH,MAAM,EAAE,SAAS;YAEjB,QAAQ,EAAE;gBASR,MAAM,EAAE,OAAO;gBAWf,aAAa,EAAE,IAAI;gBAYnB,IAAI,EAAE,CAAC,OAAO,CAAC;gBAaf,KAAK,EAAE,GAAG,EAAE,yEAAQ,iBAAiB,GAAC;aACvC;SACF;KACF;CACF,CAAA;AAED,kBAAe,UAAU,CAAA"}
+31
View File
@@ -0,0 +1,31 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const Env_1 = __importDefault(global[Symbol.for('ioc.use')]("Adonis/Core/Env"));
const Application_1 = __importDefault(global[Symbol.for('ioc.use')]("Adonis/Core/Application"));
const databaseConfig = {
connection: Env_1.default.get('DB_CONNECTION'),
connections: {
sqlite: {
client: 'sqlite',
connection: {
filename: Application_1.default.tmpPath('db.sqlite3'),
},
pool: {
afterCreate: (conn, cb) => {
conn.run('PRAGMA foreign_keys=true', cb);
}
},
migrations: {
naturalSort: true,
},
useNullAsDefault: true,
healthCheck: false,
debug: false,
},
}
};
exports.default = databaseConfig;
//# sourceMappingURL=database.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../config/database.ts"],"names":[],"mappings":";;;;;AAOA,gFAAsC;AACtC,gGAAsD;AAGtD,MAAM,cAAc,GAAmB;IAWrC,UAAU,EAAE,aAAG,CAAC,GAAG,CAAC,eAAe,CAAC;IAEpC,WAAW,EAAE;QAYX,MAAM,EAAE;YACN,MAAM,EAAE,QAAQ;YAChB,UAAU,EAAE;gBACV,QAAQ,EAAE,qBAAW,CAAC,OAAO,CAAC,YAAY,CAAC;aAC5C;YACD,IAAI,EAAE;gBACJ,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE;oBACxB,IAAI,CAAC,GAAG,CAAC,0BAA0B,EAAE,EAAE,CAAC,CAAA;gBAC1C,CAAC;aACF;YACD,UAAU,EAAE;gBACV,WAAW,EAAE,IAAI;aAClB;YACD,gBAAgB,EAAE,IAAI;YACtB,WAAW,EAAE,KAAK;YAClB,KAAK,EAAE,KAAK;SACb;KAEF;CACF,CAAA;AAED,kBAAe,cAAc,CAAA"}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=auth.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../contracts/auth.ts"],"names":[],"mappings":""}
+1
View File
@@ -0,0 +1 @@
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../database/factories/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const Schema_1 = __importDefault(global[Symbol.for('ioc.use')]("Adonis/Lucid/Schema"));
class default_1 extends Schema_1.default {
constructor() {
super(...arguments);
this.tableName = 'users';
}
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary();
table.string('pseudo', 50).notNullable();
table.string('email', 255).notNullable().unique();
table.string('password', 180).notNullable();
table.json('avatar');
table.string('remember_me_token').nullable();
table.timestamp('created_at', { useTz: true }).notNullable();
table.timestamp('updated_at', { useTz: true }).notNullable();
});
}
async down() {
this.schema.dropTable(this.tableName);
}
}
exports.default = default_1;
//# sourceMappingURL=1701712115317_users.js.map
@@ -0,0 +1 @@
{"version":3,"file":"1701712115317_users.js","sourceRoot":"","sources":["../../../database/migrations/1701712115317_users.ts"],"names":[],"mappings":";;;;;AAAA,uFAAiD;AAEjD,eAAqB,SAAQ,gBAAU;IAAvC;;QACY,cAAS,GAAG,OAAO,CAAA;IAoB/B,CAAC;IAlBQ,KAAK,CAAC,EAAE;QACb,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YAChD,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAA;YAEhC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;YACxC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,CAAA;YACjD,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAA;YAC3C,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YAEpB,KAAK,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,QAAQ,EAAE,CAAA;YAC5C,KAAK,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;YAC5D,KAAK,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;QAC9D,CAAC,CAAC,CAAA;IACJ,CAAC;IAEM,KAAK,CAAC,IAAI;QACf,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACvC,CAAC;CACF;AArBD,4BAqBC"}
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+46
View File
@@ -0,0 +1,46 @@
@layout('layouts/main')
@set('title', 'Login')
@section('body')
<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
<img class="mx-auto h-10 w-auto" src="/favicon.ico">
<h2 class="mt-10 text-center text-2xl font-bold leading-9 tracking-tight">Sign in to your account</h2>
</div>
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
@component('components/form/form', {
'action': '',
'method': 'post',
})
@!component('components/form/field', {
'label': 'E-mail :',
'name': 'email',
'type': 'email',
'required': true,
'placeholder': 'exemple@mail.com',
})
@!component('components/form/field', {
'label': 'Mot de passe :',
'name': 'password',
'type': 'password',
'required': true,
'placeholder': 'Votre mot de passe',
})
@!component('components/form/button', {
'type': 'submit',
'text': 'Envoyer',
'style': 'flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600',
})
@end
<p class="mt-10 text-center text-sm text-gray-500">
Not a member?
<a href="{{ route('signup') }}" class="font-semibold leading-6 text-indigo-600 hover:text-indigo-500">Create your account</a>
</p>
</div>
</div>
@end
+60
View File
@@ -0,0 +1,60 @@
@layout('layouts/main')
@set('title', 'Signup')
@section('body')
<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
<img class="mx-auto h-10 w-auto" src="/favicon.ico">
<h2 class="mt-10 text-center text-2xl font-bold leading-9 tracking-tight">Create your account</h2>
</div>
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
@component('components/form/form', {
'action': '',
'method': 'post',
})
@!component('components/form/field', {
'label': 'Pseudo :',
'name': 'pseudo',
'type': 'text',
'required': true,
'placeholder': 'Votre nom',
})
@!component('components/form/field', {
'label': 'E-mail :',
'name': 'email',
'type': 'email',
'required': true,
'placeholder': 'exemple@mail.com',
})
@!component('components/form/field', {
'label': 'Mot de passe :',
'name': 'password',
'type': 'password',
'required': true,
'placeholder': 'Votre mot de passe',
})
@!component('components/form/field', {
'label': 'Confirmer le mot de passe :',
'name': 'password_confirmation',
'type': 'password',
'required': true,
'placeholder': 'Votre mot de passe',
})
@!component('components/form/button', {
'type': 'submit',
'text': 'Envoyer',
'style': 'flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600',
})
@end
<p class="mt-10 text-center text-sm text-gray-500">
Not a member?
<a href="{{ route('login') }}" class="font-semibold leading-6 text-indigo-600 hover:text-indigo-500">Sign in to your account</a>
</p>
</div>
</div>
@end
@@ -0,0 +1,71 @@
@if(flashMessages.has('success'))
<div class="flash-success">
{{ flashMessages.get('success') }}
</div>
@end
@if(flashMessages.has('error'))
<div class="flash-error">
{{ flashMessages.get('error') }}
</div>
@end
<style>
.flash-success {
color: #155724; /* Couleur du texte */
background-color: #d4edda; /* Couleur de l'arrière-plan */
border-color: #c3e6cb; /* Couleur de la bordure */
/* Style de la bordure */
border: 1px solid transparent;
border-radius: 0.25rem;
padding: 0.75rem 1.25rem; /* Espacement interne */
/* Styles supplémentaires pour le texte et les liens */
font-weight: bold;
text-align: center;
text-decoration: none;
/* Ombre légère */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.flash-error {
color: #721c24; /* Couleur du texte */
background-color: #f8d7da; /* Couleur de l'arrière-plan */
border-color: #f5c6cb; /* Couleur de la bordure */
/* Style de la bordure */
border: 1px solid transparent;
border-radius: 0.25rem;
padding: 0.75rem 1.25rem; /* Espacement interne */
/* Styles supplémentaires pour le texte et les liens */
font-weight: bold;
text-align: center;
text-decoration: none;
/* Ombre légère */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
</style>
@if(flashMessages.has('errors.pseudo'))
<div class="flash-error">
{{ flashMessages.get('errors.pseudo') }}
</div>
@end
@if(flashMessages.has('errors.email'))
<div class="flash-error">
{{ flashMessages.get('errors.email') }}
</div>
@end
@if(flashMessages.has('errors.password'))
<div class="flash-error">
{{ flashMessages.get('errors.password') }}
</div>
@end
@@ -0,0 +1,12 @@
<button
id="{{ name }}"
name="{{ name }}"
type="{{ type ? type : 'submit' }}"
@if(style)
class="{{ style }}"
@else
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
@end
>
{{ text }}
</button>
@@ -0,0 +1,30 @@
<div class="mb-4">
<label for="{{ name }}" class="block text-gray-700 text-sm font-bold mb-2">{{ label }}</label>
@if(type === 'textarea')
<textarea
id="{{ name }}"
name="{{ name }}"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
@if(required)
required
@end
@if(rows)
rows="{{ rows }}"
@end
></textarea>
@else
<input
id="{{ name }}"
name="{{ name }}"
type="{{ type ? type : 'text' }}"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
@if(required)
required
@end
@if(placeholder)
placeholder="{{ placeholder }}"
@end
>
@end
</div>
@@ -0,0 +1,7 @@
<form method="{{ method }}" action="{{ action }}">
@if(flash)
@else
@!component('components/flash')
@end
{{{ await $slots.main() }}}
</form>
@@ -0,0 +1,7 @@
<li>
<a href="{{ href }}" target="_blank">
<div class="flex text-left items-center py-5 pl-5 my-3 w-full bg-gray-300 text-gray-600 rounded-lg font-bold hover:bg-gray-700 hover:text-gray-400 transition duration-300 ease-in-out">
<img src="{{ img }}" alt="Logo" class="w-16 pr-4">{{ title }}
</div>
</a>
</li>
@@ -0,0 +1,3 @@
<div class="h-72 w-72 m-1 transition-transform border-2 border-black rounded-xl hover:scale-125 overflow-hidden duration-500 bg-white hover:z-10">
<a href="{{ href }}" target="_blank"><img src="{{ img }}" alt="Logo"></a>
</div>
@@ -0,0 +1,139 @@
<div x-data="{ depli: false }">
<button @click="depli = !depli" class="cursor-pointer rounded-full bg-none p-1.5 focus:ring-2 focus:ring-white">
<span class="flex items-center">
<svg x-cloak x-bind:class="localStorage.theme === 'dark' ? '' : 'hidden'" viewBox="0 0 24 24" fill="none"
class="w-6 h-6">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M17.715 15.15A6.5 6.5 0 0 1 9 6.035C6.106 6.922 4 9.645 4 12.867c0 3.94 3.153 7.136 7.042 7.136 3.101 0 5.734-2.032 6.673-4.853Z"
class="fill-sky-400/20"></path>
<path
d="m17.715 15.15.95.316a1 1 0 0 0-1.445-1.185l.495.869ZM9 6.035l.846.534a1 1 0 0 0-1.14-1.49L9 6.035Zm8.221 8.246a5.47 5.47 0 0 1-2.72.718v2a7.47 7.47 0 0 0 3.71-.98l-.99-1.738Zm-2.72.718A5.5 5.5 0 0 1 9 9.5H7a7.5 7.5 0 0 0 7.5 7.5v-2ZM9 9.5c0-1.079.31-2.082.845-2.93L8.153 5.5A7.47 7.47 0 0 0 7 9.5h2Zm-4 3.368C5 10.089 6.815 7.75 9.292 6.99L8.706 5.08C5.397 6.094 3 9.201 3 12.867h2Zm6.042 6.136C7.718 19.003 5 16.268 5 12.867H3c0 4.48 3.588 8.136 8.042 8.136v-2Zm5.725-4.17c-.81 2.433-3.074 4.17-5.725 4.17v2c3.552 0 6.553-2.327 7.622-5.537l-1.897-.632Z"
class="fill-sky-500"></path>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M17 3a1 1 0 0 1 1 1 2 2 0 0 0 2 2 1 1 0 1 1 0 2 2 2 0 0 0-2 2 1 1 0 1 1-2 0 2 2 0 0 0-2-2 1 1 0 1 1 0-2 2 2 0 0 0 2-2 1 1 0 0 1 1-1Z"
class="fill-sky-500"></path>
</svg>
<svg x-cloak x-bind:class="localStorage.theme === 'light' ? '' : 'hidden'" viewBox="0 0 24 24" fill="none"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6">
<path d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" class="fill-sky-400/20 stroke-sky-500"></path>
<path
d="M12 4v1M17.66 6.344l-.828.828M20.005 12.004h-1M17.66 17.664l-.828-.828M12 20.01V19M6.34 17.664l.835-.836M3.995 12.004h1.01M6 6l.835.836"
class="stroke-sky-500"></path>
</svg>
<svg x-cloak
x-bind:class="localStorage.theme === 'systeme' && window.matchMedia('(prefers-color-scheme: dark)').matches ? '' : 'hidden'"
viewBox="0 0 24 24" fill="none" class="w-6 h-6 ">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M17.715 15.15A6.5 6.5 0 0 1 9 6.035C6.106 6.922 4 9.645 4 12.867c0 3.94 3.153 7.136 7.042 7.136 3.101 0 5.734-2.032 6.673-4.853Z"
class="fill-transparent"></path>
<path
d="m17.715 15.15.95.316a1 1 0 0 0-1.445-1.185l.495.869ZM9 6.035l.846.534a1 1 0 0 0-1.14-1.49L9 6.035Zm8.221 8.246a5.47 5.47 0 0 1-2.72.718v2a7.47 7.47 0 0 0 3.71-.98l-.99-1.738Zm-2.72.718A5.5 5.5 0 0 1 9 9.5H7a7.5 7.5 0 0 0 7.5 7.5v-2ZM9 9.5c0-1.079.31-2.082.845-2.93L8.153 5.5A7.47 7.47 0 0 0 7 9.5h2Zm-4 3.368C5 10.089 6.815 7.75 9.292 6.99L8.706 5.08C5.397 6.094 3 9.201 3 12.867h2Zm6.042 6.136C7.718 19.003 5 16.268 5 12.867H3c0 4.48 3.588 8.136 8.042 8.136v-2Zm5.725-4.17c-.81 2.433-3.074 4.17-5.725 4.17v2c3.552 0 6.553-2.327 7.622-5.537l-1.897-.632Z"
class="fill-slate-400 dark:fill-slate-500"></path>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M17 3a1 1 0 0 1 1 1 2 2 0 0 0 2 2 1 1 0 1 1 0 2 2 2 0 0 0-2 2 1 1 0 1 1-2 0 2 2 0 0 0-2-2 1 1 0 1 1 0-2 2 2 0 0 0 2-2 1 1 0 0 1 1-1Z"
class="fill-slate-400 dark:fill-slate-500"></path>
</svg>
<svg x-cloak
x-bind:class="localStorage.theme === 'systeme' && window.matchMedia('(prefers-color-scheme: dark)').matches !== true ? '' : 'hidden'"
viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="w-6 h-6">
<path d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" class="stroke-slate-400 dark:stroke-slate-500"></path>
<path
d="M12 4v1M17.66 6.344l-.828.828M20.005 12.004h-1M17.66 17.664l-.828-.828M12 20.01V19M6.34 17.664l.835-.836M3.995 12.004h1.01M6 6l.835.836"
class="stroke-slate-400 dark:stroke-slate-500"></path>
</svg>
</span>
</button>
<ul x-show="depli" @click.away="depli = false" x-cloak
class="overflow-hidden absolute right-0 z-10 mt-1 rounded-md bg-white text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
tabindex="-1" role="listbox" aria-labelledby="listbox-label" aria-activedescendant="listbox-option-3">
<li @click="
depli = false;
localStorage.theme = 'light';
window.location.reload()"
class="text-gray-900 relative cursor-default select-none py-2 pl-3 pr-9 hover:bg-gray-300" id="listbox-option-0"
role="option">
<div class="flex items-center">
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="w-6 h-6 mr-2">
<path d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" class="stroke-slate-400 dark:stroke-slate-500"></path>
<path
d="M12 4v1M17.66 6.344l-.828.828M20.005 12.004h-1M17.66 17.664l-.828-.828M12 20.01V19M6.34 17.664l.835-.836M3.995 12.004h1.01M6 6l.835.836"
class="stroke-slate-400 dark:stroke-slate-500"></path>
</svg>
<!-- Selected: "font-semibold", Not Selected: "font-normal" -->
<span class="font-normal ml-3 block truncate">Light</span>
</div>
<span x-bind:class="localStorage.theme === 'light' ? '' : 'hidden'"
class="text-indigo-600 absolute inset-y-0 right-0 flex items-center pr-4">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd" />
</svg>
</span>
</li>
<li @click="
depli = false;
localStorage.theme = 'dark';
window.location.reload()"
class="text-gray-900 relative cursor-default select-none py-2 pl-3 pr-9 hover:bg-gray-300" id="listbox-option-0"
role="option">
<div class="flex items-center">
<svg viewBox="0 0 24 24" fill="none" class="w-6 h-6 mr-2">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M17.715 15.15A6.5 6.5 0 0 1 9 6.035C6.106 6.922 4 9.645 4 12.867c0 3.94 3.153 7.136 7.042 7.136 3.101 0 5.734-2.032 6.673-4.853Z"
class="fill-transparent"></path>
<path
d="m17.715 15.15.95.316a1 1 0 0 0-1.445-1.185l.495.869ZM9 6.035l.846.534a1 1 0 0 0-1.14-1.49L9 6.035Zm8.221 8.246a5.47 5.47 0 0 1-2.72.718v2a7.47 7.47 0 0 0 3.71-.98l-.99-1.738Zm-2.72.718A5.5 5.5 0 0 1 9 9.5H7a7.5 7.5 0 0 0 7.5 7.5v-2ZM9 9.5c0-1.079.31-2.082.845-2.93L8.153 5.5A7.47 7.47 0 0 0 7 9.5h2Zm-4 3.368C5 10.089 6.815 7.75 9.292 6.99L8.706 5.08C5.397 6.094 3 9.201 3 12.867h2Zm6.042 6.136C7.718 19.003 5 16.268 5 12.867H3c0 4.48 3.588 8.136 8.042 8.136v-2Zm5.725-4.17c-.81 2.433-3.074 4.17-5.725 4.17v2c3.552 0 6.553-2.327 7.622-5.537l-1.897-.632Z"
class="fill-slate-400 dark:fill-slate-500"></path>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M17 3a1 1 0 0 1 1 1 2 2 0 0 0 2 2 1 1 0 1 1 0 2 2 2 0 0 0-2 2 1 1 0 1 1-2 0 2 2 0 0 0-2-2 1 1 0 1 1 0-2 2 2 0 0 0 2-2 1 1 0 0 1 1-1Z"
class="fill-slate-400 dark:fill-slate-500"></path>
</svg>
<!-- Selected: "font-semibold", Not Selected: "font-normal" -->
<span class="font-normal ml-3 block truncate">Dark</span>
</div>
<span x-bind:class="localStorage.theme === 'dark' ? '' : 'hidden'"
class="text-indigo-600 absolute inset-y-0 right-0 flex items-center pr-4">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd" />
</svg>
</span>
</li>
<li @click="
depli = false;
localStorage.theme = 'systeme';
window.location.reload()"
class="text-gray-900 relative cursor-default select-none py-2 pl-3 pr-9 hover:bg-gray-300" id="listbox-option-0"
role="option">
<div class="flex items-center">
<svg viewBox="0 0 24 24" fill="none" class="w-6 h-6 mr-2">
<path d="M4 6a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6Z" stroke-width="2"
stroke-linejoin="round" class="stroke-slate-400 dark:stroke-slate-500"></path>
<path d="M14 15c0 3 2 5 2 5H8s2-2 2-5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="stroke-slate-400 dark:stroke-slate-500"></path>
</svg>
<!-- Selected: "font-semibold", Not Selected: "font-normal" -->
<span class="font-normal ml-3 block truncate">Systeme</span>
</div>
<span x-bind:class="localStorage.theme === 'systeme' ? '' : 'hidden'"
class="text-indigo-600 absolute inset-y-0 right-0 flex items-center pr-4">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd" />
</svg>
</span>
</li>
</ul>
</div>
+102
View File
@@ -0,0 +1,102 @@
@layout('layouts/main')
@set('title', 'Compte')
@section('body')
<main class="m-24">
@!component('components/flash')
<h1 class="text-4xl font-bold mb-9">Welcome {{ auth.user.pseudo }}</h1>
<a href="{{ route('home') }}"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
>return to home page</a>
<div>
<h2 class="text-2xl font-bold mb-3 mt-9">Here is your personal information:</h2>
<p>pseudo : {{ auth.user.pseudo }}</p>
<p>email : {{ auth.user.email }}</p>
<p>created at : <span id="formattedDate"></span></p>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
const createdAt = "{{ auth.user.createdAt }}";
const maDate = createdAt.substring(8, 10) + '/' + createdAt.substring(5, 7) + '/' + createdAt.substring(0, 4);
document.getElementById('formattedDate').innerText = maDate;
});
</script>
<div id="settings">
<h2 class="text-2xl font-bold mb-5 mt-9">You can modify your personal information:</h2>
@component('components/form/form', {
'action': 'modifpseudo',
'method': 'post',
'flash': 'not',
})
@!component('components/form/field', {
'label': 'Pseudo :',
'name': 'pseudo',
'type': 'text',
'required': true,
'placeholder': 'new pseudo',
})
@!component('components/form/button', {
'type': 'submit',
'text': 'Envoyer',
})
@end
<div class="mt-5"></div>
@component('components/form/form', {
'action': 'modifemail',
'method': 'post',
'flash': 'not',
})
@!component('components/form/field', {
'label': 'Email :',
'name': 'email',
'type': 'text',
'required': true,
'placeholder': 'new email',
})
@!component('components/form/button', {
'type': 'submit',
'text': 'Envoyer',
})
@end
</div>
<div class="mt-12">
<img class="h-32 w-32 rounded-full" src="{{ auth.user.avatarUrl }}" alt="">
<form action="" method="post" enctype="multipart/form-data" class="mt-4">
<label for="avatar">Avatar :</label>
<input type="file" name="avatar" id="avatar">
<button
type="submit"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mt-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
>Valider
</button>
</form>
</div>
<div class="mt-12">
<h2 class="text-2xl font-bold mb-3 mt-9">Delete your compte :</h2>
<a href="{{ route('delete') }}"
class="inline-block bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50">
Delete
</a>
</div>
</main>
@end
+17
View File
@@ -0,0 +1,17 @@
@layout('layouts/main')
@set('title', 'Arthur - Portfolio')
@section('body')
@include('partials/nav')
@include('partials/header')
<main>
@include('partials/main/about')
@include('partials/main/projects')
@include('partials/main/contact')
</main>
@include('partials/footer')
@end
+14
View File
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{ title }}</title>
@entryPointStyles('app')
@entryPointScripts('app')
</head>
<body class="bg-gray-100 dark:bg-slate-900 text-slate-900 dark:text-white">
@!section('body')
</body>
</html>
@@ -0,0 +1,22 @@
<footer
id="footer"
class="font-semibold min-h-100 flex items-center mt-16 justify-between px-20 bg-gray-800 text-gray-400 px- h-28">
<span>&copy; <span id="year"></span> - Arthur Puechberty</span>
<a href="{{ route('terms') }}" target="_blank">Terms and Conditions</a>
</footer>
<script>
var year = new Date().getFullYear();
document.getElementById("year").innerHTML = year;
document.getElementById('myForm').addEventListener('submit', function(event) {
var input = document.getElementById('myInput');
if (input.value.trim() === '') {
event.preventDefault();
} else {
this.action = "https://www.google.fr/search";
}
});
</script>
@@ -0,0 +1,43 @@
<header class="flex mt-36 mx-4 sm:mx-24">
<div class="sm:mr-10">
<h1 class="text-4xl font-bold mb-3">Hi! 👋🏻<br>I'm Arthur.</h1>
<h2 class="text-2xl font-bold mb-3">Founder</h2>
<div class="pl-4 mb-3 border-l-8 border-solid border-gray-400">
<p>I am a young Frenchman with a passion for development, I am a self-taught learner and my creativity knows no
limits. Every pixel I edit tells a story. Passionate about transforming ideas into visually captivating
experiences, I create projects that push the boundaries of design innovation.</p>
</div>
<p class="italic text-gray-600">"In the middle of every difficulty lies opportunity."</p>
<p class="italic text-gray-600 mb-10">Albert Einstein</p>
<ul>
@!component('components/list_lien', {
href: 'https://www.instagram.com/arthur.pbty/',
img: '/logo insta.png',
title: 'Instagram',
})
@!component('components/list_lien', {
href: 'https://github.com/Tutur33',
img: '/logo github.png',
title: 'Github',
})
@!component('components/list_lien', {
href: 'https://discord.gg/HxgaA44CPh',
img: '/Logo Discord.png',
title: 'Discord',
})
@!component('components/list_lien', {
href: 'http://tuturp33.000webhostapp.com',
img: '/other.png',
title: 'Other...',
})
</ul>
</div>
<div class="ml-10 md:block hidden">
<img src="/pp.jpg" alt="Profile picture" class="w-full max-w-5xl rounded-full">
</div>
</main>
</header>
@@ -0,0 +1,21 @@
<section id="about" class="pt-32 mx-4 sm:mx-24">
<h2 class="text-2xl font-bold mb-10">About</h2>
<p>Hi ! I'm Arthur, a passionate developer based in France. My journey in the world of development is an inspiring adventure. Every line of code I write is a step toward creating unique and engaging experiences. I am driven by the desire to bring ideas to life and shape them into innovative designs.</p>
<h3 class="text-xl font-semibold mt-3">My background</h3>
<p>I gained experience in web development working on various projects, ranging from front-end to back-end. I have a deep command of languages like HTML, CSS, JavaScript, and have worked with modern frameworks like Adonis with Node.js.</p>
<h3 class="text-xl font-semibold mt-3">My philosophy</h3>
<p>I firmly believe that every difficulty is an opportunity to create something new and exciting. My approach is to combine functionality and aesthetics to deliver exceptional user experiences.</p>
<h3 class="text-xl font-semibold mt-3">My skills</h3>
<p>Web development, web design, application development, project management.</p>
<h3 class="text-xl font-semibold mt-3">My interests</h3>
<p>Outside of development, I love exploring new places, photography, and reading books about technology and creativity. And go sailing!</p>
<h3 class="text-xl font-semibold mt-3">My vision</h3>
<p>My visionMy goal is to continue to learn, innovate and inspire through my projects. I want to help create a better, more engaging web for everyone.</p>
<p class="mt-9">Please feel free to contact me if you have any questions or would like to collaborate on an exciting project. You can reach me via [your email address] or follow me on social media using the links below.</p>
</section>
@@ -0,0 +1,35 @@
<section id="contact" class="pt-32 mx-4 sm:mx-24">
<h2 class="text-2xl font-bold mb-10">Contact</h2>
@component('components/form/form', {
'action': '#',
'method': 'post',
})
@!component('components/form/field', {
'label': 'Nom :',
'name': 'nom',
'type': 'text',
'required': true,
'placeholder': 'nom',
})
@!component('components/form/field', {
'label': 'E-mail :',
'name': 'email',
'type': 'email',
'required': true,
'placeholder': 'exemple@mail.com',
})
@!component('components/form/field', {
'label': 'Message :',
'name': 'message',
'type': 'textarea',
'rows': 4,
'required': true,
})
@!component('components/form/button', {
'type': 'submit',
'text': 'Envoyer',
})
@end
</section>
@@ -0,0 +1,18 @@
<section id="projects" class="pt-32 mx-4 sm:mx-24">
<h2 class="text-2xl font-bold mb-10">Projects</h2>
<div class="flex flex-wrap justify-center">
@!component('components/project', {
'href': '#',
'img': '/projects/project-nups.png',
})
@!component('components/project', {
'href': '#',
'img': '/projects/my-networks.jpg',
})
@!component('components/project', {
'href': '#',
'img': '/projects/journal.avif',
})
</div>
</section>
+78
View File
@@ -0,0 +1,78 @@
<nav
x-data="{ mobileMenuOpen: false, profileMenuOpen: false }"
class="bg-gray-800 fixed top-0 left-0 w-full z-50"
>
<div class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
<div class="relative flex h-16 items-center justify-between">
<!-- Mobile menu button-->
<div class="absolute inset-y-0 left-0 flex items-center sm:hidden">
<button
@click="mobileMenuOpen = !mobileMenuOpen"
type="button"
class="relative inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
>
<!-- Mobile menu close -->
<svg x-show="!mobileMenuOpen" class="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
<!-- Mobile menu open -->
<svg x-show="mobileMenuOpen" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
<div class="flex flex-shrink-0 items-center">
<img class="h-8 w-auto" src="/favicon.ico">
</div>
<div class="hidden sm:ml-6 sm:block">
<div class="flex space-x-4">
<!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
<a href="#" class="bg-gray-900 text-white rounded-md px-3 py-2 text-sm font-medium">Accueil</a>
<a href="#about" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium transition duration-300">About</a>
<a href="#projects" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium transition duration-300">Projects</a>
<a href="#contact" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium transition duration-300">Contact</a>
</div>
</div>
</div>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
@!component('components/select_theme')
@if(auth.user)
<!-- Profile dropdown -->
<div @click="profileMenuOpen = !profileMenuOpen" class="relative ml-3">
<div>
<button type="button" class="relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" id="user-menu-button" aria-expanded="false" aria-haspopup="true">
<span class="absolute -inset-1.5"></span>
<span class="sr-only">Open user menu</span>
<img class="h-8 w-8 rounded-full" src="{{ auth.user.avatarUrl }}" alt="">
</button>
</div>
<!-- Dropdown menu -->
<div x-show="profileMenuOpen" @click.away="profileMenuOpen = false" x-cloak class="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1">
<a href="{{ route('compte') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-200">Your Profile</a>
<a href="{{ route('compte') }}#settings" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-200">Settings</a>
<a href="{{ route('logout') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-200">Sign out</a>
</div>
</div>
@else
<a href="{{ route('login') }}" class="mr-2 text-gray-300">Login</a>
<a href="{{ route('signup') }}" class="text-gray-300">Signup</a>
@end
</div>
</div>
</div>
<!-- Mobile menu -->
<div x-show="mobileMenuOpen" @click.away="mobileMenuOpen = false" x-cloak class="sm:hidden space-y-1 px-2 pb-3 pt-2" id="mobile-menu">
<a href="#" class="bg-gray-900 text-white block rounded-md px-3 py-2 text-base font-medium" aria-current="page">Accueil</a>
<a href="#about" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">About</a>
<a href="#projects" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Projects</a>
<a href="#contact" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Contact</a>
</div>
</nav>
+122
View File
@@ -0,0 +1,122 @@
@layout('layouts/main')
@set('title', 'Terms and Conditions')
@section('body')
<main class="mx-24 my-20">
<h1 class="text-4xl font-bold my-10">Nups Terms and Conditions</h1>
<p class="mb-3">Effective Date: Saturday, June 17, 2023</p>
<p class="mb-3">Thank you for using Nups services! These Terms and Conditions govern your use of our services. Please
read them carefully.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Acceptance of Terms and Conditions</h2>
<p class="mb-3">1.1 Acceptance of Terms. By using Nups services, you agree to be bound by these Terms and Conditions.
If you do not accept these Terms, you cannot use our services.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Use of Our Services</h2>
<p class="mb-3">2.1 User Account. To use certain Nups services, you will need to create a user account. You are
responsible for the confidentiality of your login credentials and all activities related to your account.</p>
<p class="mb-3">2.2 Authorized Use. You agree to use Nups services in accordance with these Terms and Conditions and
all applicable laws. You may not use our services in a way that could infringe on Nups' rights or the rights of
others.</p>
<p class="mb-3">2.3 Usage Restrictions. You agree not to access Nups services in an unauthorized manner, attempt to
gain unauthorized access to accounts or computer systems, and not to disrupt or harm Nups services.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">User Content</h2>
<p class="mb-3">3.1 Content Ownership. You retain ownership of any content you submit, post, or display on Nups
services. By submitting, posting, or displaying content, you grant Nups a worldwide, non-exclusive, transferable,
free, and royalty-free license to use, reproduce, modify, adapt, publish, translate, distribute, display, and create
derivative works from that content.</p>
<p class="mb-3">3.2 Content Responsibility. You are responsible for the content you submit, post, or display on Nups
services. You warrant that you have all necessary rights to publish this content, and it does not violate the rights
of others.</p>
<p class="mb-3">3.3 Respect for Intellectual Property Rights. You agree not to violate the intellectual property
rights of Nups or third parties when using Nups services. You may not copy, modify, distribute, or access
copyrighted content or other intellectual property items without authorization.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Intellectual Property</h2>
<p class="mb-3">4.1 Nups' Intellectual Property Rights. All intellectual property rights related to Nups services
(except user content) are owned by Nups. You do not acquire any ownership rights to Nups services or the content you
access.</p>
<p class="mb-3">4.2 Authorized Use. Subject to compliance with these Terms and Conditions, Nups grants you a limited,
non-exclusive, revocable, non-transferable license to use Nups services for personal and non-commercial purposes.
</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Privacy</h2>
<p class="mb-3">5.1 Data Collection. Nups collects and uses your information in accordance with its Privacy Policy. By
using Nups services, you consent to the collection and use of your information as outlined in this policy.</p>
<p class="mb-3">5.2 Cookies. Nups services may use cookies and similar technologies to facilitate your site usage and
personalize your experience.</p>
<p class="mb-3">5.3 Data Security. Nups implements appropriate security measures to protect your information from
unauthorized access, disclosure, or misuse.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Changes to Services</h2>
<p class="mb-3">6.1 Changes to Terms and Conditions. Nups reserves the right to modify these Terms and Conditions at
any time. Changes take effect upon their publication on the Nups website. Your continued use of Nups services after
such changes constitutes your acceptance of the amended Terms and Conditions.</p>
<p class="mb-3">6.2 Changes to Services. Nups also reserves the right to modify, suspend, or temporarily or
permanently terminate any aspect of its services, including access to certain features, without notice or liability
to you. You acknowledge that Nups will not be liable to you or any third party for any changes, suspensions, or
interruptions of its services.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Responsibilities and Limitations</h2>
<p class="mb-3">7.1 Disclaimer of Warranties. Nups services are provided "as is" and "as available," without any
warranties, express or implied. Nups does not guarantee the accuracy, completeness, reliability, or availability of
the services. You use Nups services at your own risk.</p>
<p class="mb-3">7.2 Limitation of Liability. To the fullest extent permitted by law, Nups will not be liable for
direct, indirect, incidental, special, consequential, or punitive damages arising from your use or inability to use
Nups services.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Termination</h2>
<p class="mb-3">8.1 Termination by You. You may terminate your user account and stop using Nups services at any time.
</p>
<p class="mb-3">8.2 Termination by Nups. Nups reserves the right to terminate your user account and terminate your use
of the services, in whole or in part, if you violate these Terms and Conditions or if your use of the services is
detrimental to Nups or other users.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">General Provisions</h2>
<p class="mb-3">9.1 Entire Agreement. These Terms and Conditions constitute the entire agreement between you and Nups
regarding the use of Nups services and supersede all prior or contemporaneous agreements between you and Nups.</p>
<p class="mb-3">9.2 Applicable Law. These Terms and Conditions are governed and interpreted in accordance with the
laws of the country where Nups is headquartered.</p>
<p class="mb-3">9.3 Dispute Resolution. Any dispute arising from these Terms and Conditions shall be subject to the
exclusive jurisdiction of the courts of the country where Nups is headquartered.</p>
<p class="mb-3">9.4 Severability. If any provision of these Terms and Conditions is found to be invalid, illegal, or
unenforceable by a competent court, such provision shall be modified to the minimum extent necessary to make it
valid, legal, and enforceable, and the other provisions shall remain in full force.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Communications</h2>
<p class="mb-3">10.1 Nups Communications. By using Nups services, you consent to receive communications from Nups,
including announcements, updates, promotional offers, and service-related messages.</p>
<p class="mb-3">10.2 User Communications. If you choose to contact Nups, you agree that Nups may respond to you using
the contact information you have provided.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Indemnification</h2>
<p class="mb-3">11.1 Indemnification. You agree to indemnify, defend, and hold Nups, its officers, directors,
employees, agents, and representatives harmless from any claim, liability, loss, damage, cost, or expense, including
reasonable attorney's fees, arising from your use of Nups services or your violation of these Terms and Conditions.
</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Copyright Infringement Notifications</h2>
<p class="mb-3">12.1 Copyright Infringement Notifications. Nups respects the intellectual property rights of others.
If you believe that any content available on Nups services violates your copyrights, please submit a copyright
infringement notification in accordance with applicable law.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Severability of Provisions</h2>
<p class="mb-3">13.1 Severability of Provisions. If any provision of these Terms and Conditions is found invalid,
illegal, or unenforceable by a competent court, such provision shall be modified to the minimum extent necessary to
make it valid, legal, and enforceable, and the other provisions shall remain in full force.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Language</h2>
<p class="mb-3">14.1 Language of the Terms. In case of any discrepancy between the language versions of these Terms
and Conditions, the French version shall prevail.</p>
<h2 class="text-2xl font-bold mt-8 mb-4">Contact Us</h2>
<p class="mb-3">15.1 Contact. If you have questions, concerns, or comments about these Terms and Conditions, please
contact us at the following address:</p>
<p class="mb-3">Nups Inc.<br>Address: [Address]<br>Email: [Email]<br>Phone: [Phone Number]</p>
<p class="mb-3">Thank you for reading our Terms and Conditions! We hope you enjoy using Nups services.</p>
</main>
@end