feat: Implement member self-service features for profile change requests and event responses
- Added MemberProfileChangeRequest and MemberEventResponse models with associations. - Created API endpoints for member dashboard, profile change requests, event responses, and training attendance updates. - Developed MemberHomeView component to display member-specific information and actions. - Updated routing to include new member dashboard and profile change request functionalities. - Added SQL migration scripts for new database tables related to member self-service. - Enhanced login functionality to redirect members based on their roles and club associations.
This commit is contained in:
17
backend/models/MemberEventResponse.js
Normal file
17
backend/models/MemberEventResponse.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { DataTypes } from 'sequelize';
|
||||
import sequelize from '../database.js';
|
||||
|
||||
const MemberEventResponse = sequelize.define('MemberEventResponse', {
|
||||
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
|
||||
clubId: { type: DataTypes.INTEGER, allowNull: false, field: 'club_id' },
|
||||
eventId: { type: DataTypes.INTEGER, allowNull: false, field: 'event_id' },
|
||||
memberId: { type: DataTypes.INTEGER, allowNull: false, field: 'member_id' },
|
||||
status: { type: DataTypes.ENUM('attending', 'declined'), allowNull: false },
|
||||
}, {
|
||||
tableName: 'member_event_responses',
|
||||
underscored: true,
|
||||
timestamps: true,
|
||||
indexes: [{ unique: true, fields: ['event_id', 'member_id'] }, { fields: ['club_id', 'event_id'] }],
|
||||
});
|
||||
|
||||
export default MemberEventResponse;
|
||||
21
backend/models/MemberProfileChangeRequest.js
Normal file
21
backend/models/MemberProfileChangeRequest.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import { DataTypes } from 'sequelize';
|
||||
import sequelize from '../database.js';
|
||||
|
||||
const MemberProfileChangeRequest = sequelize.define('MemberProfileChangeRequest', {
|
||||
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
|
||||
clubId: { type: DataTypes.INTEGER, allowNull: false, field: 'club_id' },
|
||||
memberId: { type: DataTypes.INTEGER, allowNull: false, field: 'member_id' },
|
||||
requestedByUserId: { type: DataTypes.INTEGER, allowNull: false, field: 'requested_by_user_id' },
|
||||
requestedChanges: { type: DataTypes.JSON, allowNull: false, field: 'requested_changes' },
|
||||
status: { type: DataTypes.ENUM('open', 'approved', 'rejected'), allowNull: false, defaultValue: 'open' },
|
||||
reviewNote: { type: DataTypes.TEXT, allowNull: true, field: 'review_note' },
|
||||
reviewedByUserId: { type: DataTypes.INTEGER, allowNull: true, field: 'reviewed_by_user_id' },
|
||||
reviewedAt: { type: DataTypes.DATE, allowNull: true, field: 'reviewed_at' },
|
||||
}, {
|
||||
tableName: 'member_profile_change_requests',
|
||||
underscored: true,
|
||||
timestamps: true,
|
||||
indexes: [{ fields: ['club_id', 'status'] }, { fields: ['member_id', 'status'] }],
|
||||
});
|
||||
|
||||
export default MemberProfileChangeRequest;
|
||||
@@ -93,6 +93,8 @@ import ClubCommunicationDeliveryLog from './ClubCommunicationDeliveryLog.js';
|
||||
import ClubCommunicationTemplate from './ClubCommunicationTemplate.js';
|
||||
import ClubDistributionGroup from './ClubDistributionGroup.js';
|
||||
import ClubDistributionGroupMember from './ClubDistributionGroupMember.js';
|
||||
import MemberProfileChangeRequest from './MemberProfileChangeRequest.js';
|
||||
import MemberEventResponse from './MemberEventResponse.js';
|
||||
// Official tournaments relations
|
||||
OfficialTournament.hasMany(OfficialCompetition, { foreignKey: 'tournamentId', as: 'competitions' });
|
||||
OfficialCompetition.belongsTo(OfficialTournament, { foreignKey: 'tournamentId', as: 'tournament' });
|
||||
@@ -500,6 +502,20 @@ ClubSepaMandate.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
Member.hasMany(ClubSepaMandate, { foreignKey: 'memberId', as: 'sepaMandates' });
|
||||
ClubSepaMandate.belongsTo(Member, { foreignKey: 'memberId', as: 'member', constraints: false });
|
||||
|
||||
Club.hasMany(MemberProfileChangeRequest, { foreignKey: 'clubId', as: 'memberProfileChangeRequests' });
|
||||
MemberProfileChangeRequest.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
Member.hasMany(MemberProfileChangeRequest, { foreignKey: 'memberId', as: 'profileChangeRequests' });
|
||||
MemberProfileChangeRequest.belongsTo(Member, { foreignKey: 'memberId', as: 'member' });
|
||||
User.hasMany(MemberProfileChangeRequest, { foreignKey: 'requestedByUserId', as: 'requestedMemberProfileChanges' });
|
||||
MemberProfileChangeRequest.belongsTo(User, { foreignKey: 'requestedByUserId', as: 'requestedByUser' });
|
||||
|
||||
Club.hasMany(MemberEventResponse, { foreignKey: 'clubId', as: 'memberEventResponses' });
|
||||
MemberEventResponse.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
CalendarEvent.hasMany(MemberEventResponse, { foreignKey: 'eventId', as: 'memberResponses' });
|
||||
MemberEventResponse.belongsTo(CalendarEvent, { foreignKey: 'eventId', as: 'event' });
|
||||
Member.hasMany(MemberEventResponse, { foreignKey: 'memberId', as: 'eventResponses' });
|
||||
MemberEventResponse.belongsTo(Member, { foreignKey: 'memberId', as: 'member' });
|
||||
|
||||
Club.hasMany(ClubPaymentClaim, { foreignKey: 'clubId', as: 'paymentClaims' });
|
||||
ClubPaymentClaim.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
Member.hasMany(ClubPaymentClaim, { foreignKey: 'memberId', as: 'paymentClaims' });
|
||||
@@ -688,5 +704,7 @@ export {
|
||||
ClubCommunicationDeliveryLog,
|
||||
ClubCommunicationTemplate,
|
||||
ClubDistributionGroup,
|
||||
MemberProfileChangeRequest,
|
||||
MemberEventResponse,
|
||||
ClubDistributionGroupMember,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user