commit
c8511db01c
1
.gitignore
vendored
1
.gitignore
vendored
@ -49,3 +49,4 @@ nb-configuration.xml
|
|||||||
/src/main/resources/META-INF/resources/
|
/src/main/resources/META-INF/resources/
|
||||||
/media/
|
/media/
|
||||||
/media-ext/
|
/media-ext/
|
||||||
|
/sign.jpg
|
||||||
|
|||||||
@ -10,6 +10,7 @@ services:
|
|||||||
- ${PWD}/ffsaf.properties:/work/config/application.properties
|
- ${PWD}/ffsaf.properties:/work/config/application.properties
|
||||||
- ${PWD}/ffsaf_cle_prive.jks:/work/cle_prive.jks
|
- ${PWD}/ffsaf_cle_prive.jks:/work/cle_prive.jks
|
||||||
- ${PWD}/mail-truststore.p12:/work/mail-truststore.p12
|
- ${PWD}/mail-truststore.p12:/work/mail-truststore.p12
|
||||||
|
- ${PWD}/sign.jpg:/work/sign.jpg
|
||||||
- ${PWD}/ffsaf-media:/work/media
|
- ${PWD}/ffsaf-media:/work/media
|
||||||
depends_on:
|
depends_on:
|
||||||
ffsaf-db:
|
ffsaf-db:
|
||||||
|
|||||||
@ -20,11 +20,18 @@ import io.smallrye.mutiny.Uni;
|
|||||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
import org.hibernate.reactive.mutiny.Mutiny;
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@WithSession
|
@WithSession
|
||||||
|
|||||||
@ -31,10 +31,16 @@ import io.smallrye.mutiny.Uni;
|
|||||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
import org.hibernate.reactive.mutiny.Mutiny;
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER;
|
import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER;
|
||||||
@ -42,6 +48,7 @@ import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER;
|
|||||||
@WithSession
|
@WithSession
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class ClubService {
|
public class ClubService {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(ClubService.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ClubRepository repository;
|
ClubRepository repository;
|
||||||
@ -61,6 +68,12 @@ public class ClubService {
|
|||||||
@Inject
|
@Inject
|
||||||
LoggerService ls;
|
LoggerService ls;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "pdf-maker.jar-path")
|
||||||
|
String pdfMakerJarPath;
|
||||||
|
|
||||||
|
@ConfigProperty(name = "pdf-maker.sign-file")
|
||||||
|
String sign_file;
|
||||||
|
|
||||||
public SimpleClubModel findByIdOptionalClub(long id) throws Throwable {
|
public SimpleClubModel findByIdOptionalClub(long id) throws Throwable {
|
||||||
return VertxContextSupport.subscribeAndAwait(
|
return VertxContextSupport.subscribeAndAwait(
|
||||||
() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel)));
|
() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel)));
|
||||||
@ -333,4 +346,120 @@ public class ClubService {
|
|||||||
return data;
|
return data;
|
||||||
}).collect().asList();
|
}).collect().asList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uni<Response> getAffiliationPdf(String subject) {
|
||||||
|
return getAffiliationPdf(
|
||||||
|
combRepository.find("userId = ?1", subject).firstResult()
|
||||||
|
.invoke(Unchecked.consumer(m -> {
|
||||||
|
if (m == null || m.getClub() == null)
|
||||||
|
throw new DNotFoundException("Club non trouvé");
|
||||||
|
}))
|
||||||
|
.map(MembreModel::getClub)
|
||||||
|
.call(m -> Mutiny.fetch(m.getAffiliations())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<Response> getAffiliationPdf(long id) {
|
||||||
|
return getAffiliationPdf(
|
||||||
|
repository.findById(id)
|
||||||
|
.invoke(Unchecked.consumer(m -> {
|
||||||
|
if (m == null)
|
||||||
|
throw new DNotFoundException("Club non trouvé");
|
||||||
|
}))
|
||||||
|
.call(m -> Mutiny.fetch(m.getAffiliations())));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Uni<Response> getAffiliationPdf(Uni<ClubModel> uniBase) {
|
||||||
|
return uniBase
|
||||||
|
.map(Unchecked.function(m -> {
|
||||||
|
if (m.getAffiliations().stream()
|
||||||
|
.noneMatch(licenceModel -> licenceModel.getSaison() == Utils.getSaison() - 1))
|
||||||
|
throw new DNotFoundException("Pas d'affiliation pour la saison en cours");
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] buff = make_pdf(m);
|
||||||
|
if (buff == null)
|
||||||
|
throw new IOException("Error making pdf");
|
||||||
|
|
||||||
|
String mimeType = "application/pdf";
|
||||||
|
|
||||||
|
Response.ResponseBuilder resp = Response.ok(buff);
|
||||||
|
resp.type(MediaType.APPLICATION_OCTET_STREAM);
|
||||||
|
resp.header(HttpHeaders.CONTENT_LENGTH, buff.length);
|
||||||
|
resp.header(HttpHeaders.CONTENT_TYPE, mimeType);
|
||||||
|
resp.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||||
|
"inline; " + "filename=\"Attestation d'affiliation " + Utils.getSaison() + "-" +
|
||||||
|
(Utils.getSaison() + 1) + " de " + m.getName() + ".pdf\"");
|
||||||
|
return resp.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] make_pdf(ClubModel m) throws IOException, InterruptedException {
|
||||||
|
List<String> cmd = new ArrayList<>();
|
||||||
|
cmd.add("java");
|
||||||
|
cmd.add("-jar");
|
||||||
|
cmd.add(pdfMakerJarPath);
|
||||||
|
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
|
cmd.add("/tmp/" + uuid + ".pdf");
|
||||||
|
cmd.add("club");
|
||||||
|
cmd.add(m.getName());
|
||||||
|
cmd.add(Utils.getSaison() + "");
|
||||||
|
cmd.add(m.getNo_affiliation() + "");
|
||||||
|
cmd.add(new File(sign_file).getAbsolutePath());
|
||||||
|
|
||||||
|
return getPdf(cmd, uuid, LOGGER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] getPdf(List<String> cmd, UUID uuid, Logger logger) throws IOException, InterruptedException {
|
||||||
|
ProcessBuilder processBuilder = new ProcessBuilder(cmd);
|
||||||
|
processBuilder.redirectErrorStream(true);
|
||||||
|
Process process = processBuilder.start();
|
||||||
|
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
Thread t = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null)
|
||||||
|
builder.append(line).append("\n");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.start();
|
||||||
|
|
||||||
|
int code = -1;
|
||||||
|
if (!process.waitFor(30, TimeUnit.SECONDS)) {
|
||||||
|
process.destroy();
|
||||||
|
builder.append("Timeout...");
|
||||||
|
} else {
|
||||||
|
code = process.exitValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.isAlive())
|
||||||
|
t.interrupt();
|
||||||
|
|
||||||
|
logger.debug("PDF maker: " + builder);
|
||||||
|
|
||||||
|
if (code != 0) {
|
||||||
|
throw new IOException("Error code: " + code);
|
||||||
|
} else {
|
||||||
|
File file = new File("/tmp/" + uuid + ".pdf");
|
||||||
|
try (FileInputStream fis = new FileInputStream(file)) {
|
||||||
|
byte[] buff = fis.readAllBytes();
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
file.delete();
|
||||||
|
return buff;
|
||||||
|
} catch (IOException e) {
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,12 +33,15 @@ import org.eclipse.microprofile.config.inject.ConfigProperty;
|
|||||||
import org.hibernate.reactive.mutiny.Mutiny;
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.io.IOException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import static fr.titionfire.ffsaf.domain.service.ClubService.getPdf;
|
||||||
|
|
||||||
|
|
||||||
@WithSession
|
@WithSession
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
@ -506,6 +509,7 @@ public class MembreService {
|
|||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
cmd.add("/tmp/" + uuid + ".pdf");
|
cmd.add("/tmp/" + uuid + ".pdf");
|
||||||
|
cmd.add("membre");
|
||||||
cmd.add(m.getFname());
|
cmd.add(m.getFname());
|
||||||
cmd.add(m.getLname());
|
cmd.add(m.getLname());
|
||||||
cmd.add(m.getGenre().str);
|
cmd.add(m.getGenre().str);
|
||||||
@ -526,50 +530,6 @@ public class MembreService {
|
|||||||
cmd.add("/dev/null");
|
cmd.add("/dev/null");
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessBuilder processBuilder = new ProcessBuilder(cmd);
|
return getPdf(cmd, uuid, LOGGER);
|
||||||
processBuilder.redirectErrorStream(true);
|
|
||||||
Process process = processBuilder.start();
|
|
||||||
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
Thread t = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null)
|
|
||||||
builder.append(line).append("\n");
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
t.start();
|
|
||||||
|
|
||||||
int code = -1;
|
|
||||||
if (!process.waitFor(30, TimeUnit.SECONDS)) {
|
|
||||||
process.destroy();
|
|
||||||
builder.append("Timeout...");
|
|
||||||
} else {
|
|
||||||
code = process.exitValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.isAlive())
|
|
||||||
t.interrupt();
|
|
||||||
|
|
||||||
LOGGER.debug("PDF maker: " + builder);
|
|
||||||
|
|
||||||
if (code != 0) {
|
|
||||||
throw new IOException("Error code: " + code);
|
|
||||||
} else {
|
|
||||||
File file = new File("/tmp/" + uuid + ".pdf");
|
|
||||||
try (FileInputStream fis = new FileInputStream(file)) {
|
|
||||||
byte[] buff = fis.readAllBytes();
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
file.delete();
|
|
||||||
return buff;
|
|
||||||
} catch (IOException e) {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -207,6 +207,21 @@ public class ClubEndpoints {
|
|||||||
return clubService.delete(id);
|
return clubService.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{id}/affiliation")
|
||||||
|
@RolesAllowed({"federation_admin"})
|
||||||
|
@Operation(summary = "Renvoie l'attestation d'affiliation du club en fonction de son identifiant", description =
|
||||||
|
"Renvoie l'attestation d'affiliation du club en fonction de son identifiant")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "L'attestation d'affiliation"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le club n'existe pas ou n'a pas d'affiliation active"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Response> getAffiliation(@Parameter(description = "Identifiant de club") @PathParam("id") long id) {
|
||||||
|
return clubService.getAffiliationPdf(id);
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/me")
|
@Path("/me")
|
||||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
@ -241,13 +256,28 @@ public class ClubEndpoints {
|
|||||||
return clubService.updateOfUser(securityCtx, form);
|
return clubService.updateOfUser(securityCtx, form);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/me/affiliation")
|
||||||
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
@Operation(summary = "Renvoie l'attestation d'affiliation du club de l'utilisateur connecté", description =
|
||||||
|
"Renvoie l'attestation d'affiliation du club de l'utilisateur connecté")
|
||||||
|
@APIResponses(value = {
|
||||||
|
@APIResponse(responseCode = "200", description = "L'attestation d'affiliation"),
|
||||||
|
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||||
|
@APIResponse(responseCode = "404", description = "Le club n'a pas d'affiliation active"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||||
|
})
|
||||||
|
public Uni<Response> getMeAffiliation() {
|
||||||
|
return clubService.getAffiliationPdf(securityCtx.getSubject());
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/renew/{id}")
|
@Path("/renew/{id}")
|
||||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Operation(hidden = true)
|
@Operation(hidden = true)
|
||||||
public Uni<RenewAffData> getRenew(@PathParam("id") long id, @QueryParam("m1") long m1_id,
|
public Uni<RenewAffData> getRenew(@PathParam("id") long id, @QueryParam("m1") long m1_id,
|
||||||
@QueryParam("m2") long m2_id, @QueryParam("m3") long m3_id) {
|
@QueryParam("m2") long m2_id, @QueryParam("m3") long m3_id) {
|
||||||
return Uni.createFrom().item(id).invoke(checkPerm2)
|
return Uni.createFrom().item(id).invoke(checkPerm2)
|
||||||
.chain(__ -> clubService.getRenewData(id, List.of(m1_id, m2_id, m3_id)));
|
.chain(__ -> clubService.getRenewData(id, List.of(m1_id, m2_id, m3_id)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
package fr.titionfire;
|
package fr.titionfire;
|
||||||
|
|
||||||
import com.lowagie.text.*;
|
import com.lowagie.text.*;
|
||||||
import com.lowagie.text.pdf.BaseFont;
|
import com.lowagie.text.Font;
|
||||||
import com.lowagie.text.pdf.PdfPCell;
|
import com.lowagie.text.Image;
|
||||||
import com.lowagie.text.pdf.PdfPTable;
|
import com.lowagie.text.pdf.*;
|
||||||
import com.lowagie.text.pdf.PdfWriter;
|
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -13,37 +13,231 @@ import java.io.InputStream;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
PdfData pdfData = new PdfData();
|
|
||||||
|
|
||||||
File dest = new File(args[0]);
|
File dest = new File(args[0]);
|
||||||
pdfData.fname = args[1];
|
|
||||||
pdfData.lname = args[2];
|
|
||||||
pdfData.genre = args[3];
|
|
||||||
pdfData.categorie = args[4];
|
|
||||||
pdfData.certificate = args[5];
|
|
||||||
pdfData.saison = Integer.parseInt(args[6]);
|
|
||||||
pdfData.licence = Integer.parseInt(args[7]);
|
|
||||||
pdfData.club = args[8];
|
|
||||||
pdfData.club_no = Integer.parseInt(args[9]);
|
|
||||||
pdfData.birth_date = args[10];
|
|
||||||
pdfData.photo_file = new File(args[11]);
|
|
||||||
|
|
||||||
try {
|
if (args[1].equals("membre")) {
|
||||||
FileOutputStream out = new FileOutputStream(dest);
|
PdfData pdfData = new PdfData();
|
||||||
new Main().make_pdf(pdfData, out);
|
pdfData.fname = args[2];
|
||||||
} catch (IOException ignored) {
|
pdfData.lname = args[3];
|
||||||
ignored.printStackTrace();
|
pdfData.genre = args[4];
|
||||||
System.exit(1);
|
pdfData.categorie = args[5];
|
||||||
|
pdfData.certificate = args[6];
|
||||||
|
pdfData.saison = Integer.parseInt(args[7]);
|
||||||
|
pdfData.licence = Integer.parseInt(args[8]);
|
||||||
|
pdfData.club = args[9];
|
||||||
|
pdfData.club_no = Integer.parseInt(args[10]);
|
||||||
|
pdfData.birth_date = args[11];
|
||||||
|
pdfData.photo_file = new File(args[12]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
FileOutputStream out = new FileOutputStream(dest);
|
||||||
|
new Main().make_pdf(pdfData, out);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PdfDataClub pdfData = new PdfDataClub();
|
||||||
|
pdfData.name = args[2];
|
||||||
|
pdfData.saison = Integer.parseInt(args[3]);
|
||||||
|
pdfData.club_no = Integer.parseInt(args[4]);
|
||||||
|
pdfData.sign_file = new File(args[5]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
FileOutputStream out = new FileOutputStream(dest);
|
||||||
|
new Main().make_pdf_club(pdfData, out);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("PDF generated successfully");
|
System.out.println("PDF generated successfully");
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class BorderEvent implements PdfPTableEvent {
|
||||||
|
public void tableLayout(PdfPTable table, float[][] widths,
|
||||||
|
float[] heights, int headerRows, int rowStart,
|
||||||
|
PdfContentByte[] canvases) {
|
||||||
|
float[] width = widths[0];
|
||||||
|
float x1 = width[0];
|
||||||
|
float x2 = width[width.length - 1];
|
||||||
|
float y1 = heights[0];
|
||||||
|
float y2 = heights[heights.length - 1];
|
||||||
|
PdfContentByte cb = canvases[PdfPTable.LINECANVAS];
|
||||||
|
cb.setLineWidth(3);
|
||||||
|
cb.rectangle(x1, y1, x2 - x1, y2 - y1);
|
||||||
|
cb.stroke();
|
||||||
|
|
||||||
|
cb.setLineWidth(1);
|
||||||
|
cb.rectangle(x1 + 4, y1 - 4, x2 - x1 - 8, y2 - y1 + 8);
|
||||||
|
cb.stroke();
|
||||||
|
|
||||||
|
cb.resetRGBColorStroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void make_pdf_club(PdfDataClub pdfData, FileOutputStream out) throws IOException {
|
||||||
|
Document document = new Document(PageSize.A4.rotate());
|
||||||
|
PdfWriter.getInstance(document, out);
|
||||||
|
document.open();
|
||||||
|
|
||||||
|
document.addCreator("FFSAF");
|
||||||
|
document.addTitle(
|
||||||
|
"Attestation d'affiliation " + pdfData.saison + "-" + (pdfData.saison + 1) + " - " + pdfData.name);
|
||||||
|
document.addCreationDate();
|
||||||
|
document.addProducer("https://www.ffsaf.fr");
|
||||||
|
|
||||||
|
InputStream fontStream = Main.class.getClassLoader().getResourceAsStream("Helvetica.ttf");
|
||||||
|
if (fontStream == null)
|
||||||
|
throw new IOException("Font file not found");
|
||||||
|
BaseFont customFont = BaseFont.createFont("Helvetica.ttf", BaseFont.WINANSI, BaseFont.EMBEDDED, true,
|
||||||
|
null, fontStream.readAllBytes());
|
||||||
|
|
||||||
|
InputStream fontStream2 = Main.class.getClassLoader().getResourceAsStream("Helvetica-Bold.ttf");
|
||||||
|
if (fontStream2 == null)
|
||||||
|
throw new IOException("Font file not found");
|
||||||
|
BaseFont customFontBold = BaseFont.createFont("Helvetica-Bold.ttf", BaseFont.WINANSI, BaseFont.EMBEDDED, true,
|
||||||
|
null, fontStream2.readAllBytes());
|
||||||
|
|
||||||
|
InputStream fontStream3 = Main.class.getClassLoader().getResourceAsStream("helvetica-light.ttf");
|
||||||
|
if (fontStream3 == null)
|
||||||
|
throw new IOException("Font file not found");
|
||||||
|
BaseFont customFontLight = BaseFont.createFont("helvetica-light.ttf", BaseFont.WINANSI, BaseFont.EMBEDDED, true,
|
||||||
|
null, fontStream3.readAllBytes());
|
||||||
|
|
||||||
|
// Creating the main table
|
||||||
|
PdfPTable mainTable = new PdfPTable(1);
|
||||||
|
mainTable.setWidthPercentage(100);
|
||||||
|
mainTable.setSpacingBefore(0f);
|
||||||
|
mainTable.setSpacingAfter(0f);
|
||||||
|
mainTable.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
|
||||||
|
mainTable.setExtendLastRow(true);
|
||||||
|
|
||||||
|
mainTable.setTableEvent(new BorderEvent());
|
||||||
|
|
||||||
|
// Creating the main table
|
||||||
|
PdfPTable headerRow = new PdfPTable(2);
|
||||||
|
headerRow.setWidths(new float[]{1, 2});
|
||||||
|
headerRow.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
|
||||||
|
|
||||||
|
// Adding logo
|
||||||
|
Image logo = Image.getInstance(
|
||||||
|
Objects.requireNonNull(
|
||||||
|
getClass().getClassLoader().getResource("Logo-FFSAF-2023.png")));
|
||||||
|
logo.scaleToFit(160, 160);
|
||||||
|
PdfPCell logoCell = new PdfPCell(logo);
|
||||||
|
logoCell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||||
|
logoCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||||
|
logoCell.setPadding(0);
|
||||||
|
logoCell.setRowspan(1);
|
||||||
|
logoCell.setPaddingTop(20);
|
||||||
|
logoCell.setPaddingBottom(20);
|
||||||
|
logoCell.setBorder(PdfPCell.NO_BORDER);
|
||||||
|
headerRow.addCell(logoCell);
|
||||||
|
|
||||||
|
List<Paragraph> pList = new ArrayList<>();
|
||||||
|
pList.add(new Paragraph("Fédération", new Font(customFontBold, 16, Font.NORMAL)));
|
||||||
|
pList.add(new Paragraph("FRANCE SOFT ARMORED FIGHTING", new Font(customFontBold, 18, Font.NORMAL)));
|
||||||
|
pList.add(new Paragraph("Association loi 1901 n° W633001595\n ", new Font(customFontLight, 11, Font.NORMAL)));
|
||||||
|
pList.add(new Paragraph("5 place de la Barreyre", new Font(customFont, 11, Font.NORMAL)));
|
||||||
|
pList.add(new Paragraph("63320 Champeix\n ", new Font(customFont, 11, Font.NORMAL)));
|
||||||
|
pList.add(new Paragraph("ffsaf.fr", new Font(customFontBold, 14, Font.NORMAL)));
|
||||||
|
|
||||||
|
PdfPCell headerCell = new PdfPCell();
|
||||||
|
pList.forEach(p -> p.setAlignment(Element.ALIGN_CENTER));
|
||||||
|
pList.forEach(headerCell::addElement);
|
||||||
|
headerCell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||||
|
headerCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||||
|
headerCell.setBorder(PdfPCell.NO_BORDER);
|
||||||
|
headerRow.addCell(headerCell);
|
||||||
|
mainTable.addCell(headerRow);
|
||||||
|
|
||||||
|
pList.clear();
|
||||||
|
pList.add(new Paragraph("CLUB AFFILIÉ SAISON " + pdfData.saison + "-" + (pdfData.saison + 1) + "\n ",
|
||||||
|
new Font(customFontBold, 24, Font.NORMAL)));
|
||||||
|
pList.add(new Paragraph(pdfData.name, new Font(customFontBold, 20, Font.NORMAL, new Color(0, 112, 192))));
|
||||||
|
pList.add(new Paragraph(String.format("- association n°%03d -\n ", pdfData.club_no),
|
||||||
|
new Font(customFont, 18, Font.TIMES_ROMAN)));
|
||||||
|
|
||||||
|
PdfPCell bodyCell = new PdfPCell();
|
||||||
|
pList.forEach(p -> p.setAlignment(Element.ALIGN_CENTER));
|
||||||
|
pList.forEach(bodyCell::addElement);
|
||||||
|
bodyCell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||||
|
bodyCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||||
|
bodyCell.setBorder(PdfPCell.NO_BORDER);
|
||||||
|
mainTable.addCell(bodyCell);
|
||||||
|
|
||||||
|
PdfPTable footerRow = new PdfPTable(2);
|
||||||
|
footerRow.setWidths(new float[]{2, 1});
|
||||||
|
footerRow.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
|
||||||
|
|
||||||
|
|
||||||
|
PdfPTable footerSubRow = new PdfPTable(2);
|
||||||
|
footerSubRow.setWidths(new float[]{1, 5});
|
||||||
|
footerSubRow.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
|
||||||
|
|
||||||
|
Image logo2 = Image.getInstance(
|
||||||
|
Objects.requireNonNull(
|
||||||
|
getClass().getClassLoader().getResource("safef.jpg")));
|
||||||
|
logo2.scaleToFit(65, 65);
|
||||||
|
PdfPCell logo2Cell = new PdfPCell(logo2);
|
||||||
|
logo2Cell.setHorizontalAlignment(Element.ALIGN_LEFT);
|
||||||
|
logo2Cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||||
|
logo2Cell.setPadding(0);
|
||||||
|
logo2Cell.setRowspan(1);
|
||||||
|
logo2Cell.setPaddingLeft(7);
|
||||||
|
logo2Cell.setBorder(PdfPCell.NO_BORDER);
|
||||||
|
footerSubRow.addCell(logo2Cell);
|
||||||
|
|
||||||
|
pList.clear();
|
||||||
|
pList.add(new Paragraph("La FFSAF est fondatrice et affilié à la SAFE Federation.",
|
||||||
|
new Font(customFontBold, 10, Font.NORMAL)));
|
||||||
|
pList.add(new Paragraph("Soft Armored Fighting European Federation",
|
||||||
|
new Font(customFontLight, 10, Font.TIMES_ROMAN)));
|
||||||
|
|
||||||
|
PdfPCell footerCell = new PdfPCell();
|
||||||
|
pList.forEach(p -> p.setAlignment(Element.ALIGN_LEFT));
|
||||||
|
pList.forEach(footerCell::addElement);
|
||||||
|
footerCell.setHorizontalAlignment(Element.ALIGN_LEFT);
|
||||||
|
footerCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||||
|
footerCell.setBorder(PdfPCell.NO_BORDER);
|
||||||
|
footerSubRow.addCell(footerCell);
|
||||||
|
footerRow.addCell(footerSubRow);
|
||||||
|
|
||||||
|
pList.clear();
|
||||||
|
pList.add(new Paragraph("Le président de la FFSAF,", new Font(customFont, 13, Font.NORMAL)));
|
||||||
|
pList.add(new Paragraph("Guillaume Andrieux", new Font(customFontBold, 13, Font.NORMAL)));
|
||||||
|
|
||||||
|
PdfPCell footerCell2 = new PdfPCell();
|
||||||
|
pList.forEach(p -> p.setAlignment(Element.ALIGN_LEFT));
|
||||||
|
pList.forEach(footerCell2::addElement);
|
||||||
|
|
||||||
|
Image sign = Image.getInstance(Files.readAllBytes(pdfData.sign_file.toPath()));
|
||||||
|
sign.scaleToFit(120, 60);
|
||||||
|
sign.setSpacingBefore(10);
|
||||||
|
footerCell2.addElement(sign);
|
||||||
|
|
||||||
|
footerCell2.setHorizontalAlignment(Element.ALIGN_LEFT);
|
||||||
|
footerCell2.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||||
|
footerCell2.setBorder(PdfPCell.NO_BORDER);
|
||||||
|
footerRow.addCell(footerCell2);
|
||||||
|
|
||||||
|
mainTable.addCell(footerRow);
|
||||||
|
document.add(mainTable);
|
||||||
|
|
||||||
|
// Close the document
|
||||||
|
document.close();
|
||||||
|
}
|
||||||
|
|
||||||
private void make_pdf(PdfData pdfData, FileOutputStream out) throws IOException {
|
private void make_pdf(PdfData pdfData, FileOutputStream out) throws IOException {
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
|
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
|
||||||
Document document = new Document();
|
Document document = new Document();
|
||||||
@ -81,7 +275,7 @@ public class Main {
|
|||||||
// Adding logo
|
// Adding logo
|
||||||
Image logo = Image.getInstance(
|
Image logo = Image.getInstance(
|
||||||
Objects.requireNonNull(
|
Objects.requireNonNull(
|
||||||
getClass().getClassLoader().getResource("FFSSAF-bord-blanc-fond-transparent.png")));
|
getClass().getClassLoader().getResource("Logo-FFSAF-2023.png")));
|
||||||
logo.scaleToFit(120, 120);
|
logo.scaleToFit(120, 120);
|
||||||
PdfPCell logoCell = new PdfPCell(logo);
|
PdfPCell logoCell = new PdfPCell(logo);
|
||||||
logoCell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
logoCell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||||
@ -135,7 +329,8 @@ public class Main {
|
|||||||
|
|
||||||
// Adding member photo
|
// Adding member photo
|
||||||
Image memberPhoto;
|
Image memberPhoto;
|
||||||
if (pdfData.photo_file != null && pdfData.photo_file.exists() && pdfData.photo_file.getAbsolutePath().equals("/dev/null")) {
|
if (pdfData.photo_file != null && pdfData.photo_file.exists() && !pdfData.photo_file.getAbsolutePath()
|
||||||
|
.equals("/dev/null")) {
|
||||||
memberPhoto = Image.getInstance(Files.readAllBytes(pdfData.photo_file.toPath()));
|
memberPhoto = Image.getInstance(Files.readAllBytes(pdfData.photo_file.toPath()));
|
||||||
} else {
|
} else {
|
||||||
memberPhoto = Image.getInstance(
|
memberPhoto = Image.getInstance(
|
||||||
@ -168,7 +363,7 @@ public class Main {
|
|||||||
// Adding member details
|
// Adding member details
|
||||||
memberTable.addCell(new Phrase("NOM : " + pdfData.lname.toUpperCase(), bodyFont));
|
memberTable.addCell(new Phrase("NOM : " + pdfData.lname.toUpperCase(), bodyFont));
|
||||||
memberTable.addCell(new Phrase("Prénom : " + pdfData.fname, bodyFont));
|
memberTable.addCell(new Phrase("Prénom : " + pdfData.fname, bodyFont));
|
||||||
memberTable.addCell(new Phrase("Licence n° : " + pdfData.licence, bodyFont));
|
memberTable.addCell(new Phrase(String.format("Licence n° : %05d", pdfData.licence), bodyFont));
|
||||||
memberTable.addCell(new Phrase("Certificat médical par " + cert[0] + ", le " + cert[1], bodyFont));
|
memberTable.addCell(new Phrase("Certificat médical par " + cert[0] + ", le " + cert[1], bodyFont));
|
||||||
memberTable.addCell(new Phrase("")); // Empty cell for spacing
|
memberTable.addCell(new Phrase("")); // Empty cell for spacing
|
||||||
|
|
||||||
@ -180,7 +375,7 @@ public class Main {
|
|||||||
Paragraph memberClub = new Paragraph("CLUB : " + pdfData.club.toUpperCase(), bodyFont);
|
Paragraph memberClub = new Paragraph("CLUB : " + pdfData.club.toUpperCase(), bodyFont);
|
||||||
document.add(memberClub);
|
document.add(memberClub);
|
||||||
|
|
||||||
Paragraph memberClubNumber = new Paragraph("N° club : " + pdfData.club_no, bodyFont);
|
Paragraph memberClubNumber = new Paragraph(String.format("N° club : %03d ", pdfData.club_no), bodyFont);
|
||||||
document.add(memberClubNumber);
|
document.add(memberClubNumber);
|
||||||
|
|
||||||
// Adding spacing
|
// Adding spacing
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
package fr.titionfire;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class PdfDataClub {
|
||||||
|
int saison;
|
||||||
|
int club_no;
|
||||||
|
String name;
|
||||||
|
public File sign_file;
|
||||||
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 63 KiB |
BIN
src/main/pdf_gen/src/main/resources/Helvetica-Bold.ttf
Normal file
BIN
src/main/pdf_gen/src/main/resources/Helvetica-Bold.ttf
Normal file
Binary file not shown.
BIN
src/main/pdf_gen/src/main/resources/Helvetica.ttf
Normal file
BIN
src/main/pdf_gen/src/main/resources/Helvetica.ttf
Normal file
Binary file not shown.
BIN
src/main/pdf_gen/src/main/resources/Logo-FFSAF-2023.png
Normal file
BIN
src/main/pdf_gen/src/main/resources/Logo-FFSAF-2023.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 155 KiB |
BIN
src/main/pdf_gen/src/main/resources/helvetica-light.ttf
Normal file
BIN
src/main/pdf_gen/src/main/resources/helvetica-light.ttf
Normal file
Binary file not shown.
BIN
src/main/pdf_gen/src/main/resources/safef.jpg
Normal file
BIN
src/main/pdf_gen/src/main/resources/safef.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@ -14,6 +14,7 @@ quarkus.quartz.start-mode=forced
|
|||||||
|
|
||||||
%dev.pdf-maker.jar-path=src\\main\\pdf_gen\\target\\pdf_gen-1.0-SNAPSHOT-jar-with-dependencies.jar
|
%dev.pdf-maker.jar-path=src\\main\\pdf_gen\\target\\pdf_gen-1.0-SNAPSHOT-jar-with-dependencies.jar
|
||||||
pdf-maker.jar-path=/work/make_pdf.jar
|
pdf-maker.jar-path=/work/make_pdf.jar
|
||||||
|
pdf-maker.sign-file=/work/sign.jpg
|
||||||
%dev.quarkus.log.min-level=ALL
|
%dev.quarkus.log.min-level=ALL
|
||||||
%dev.quarkus.log.category."fr.titionfire.ffsaf".level=ALL
|
%dev.quarkus.log.category."fr.titionfire.ffsaf".level=ALL
|
||||||
|
|
||||||
|
|||||||
@ -2,13 +2,15 @@ import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
|||||||
import {useFetch} from "../../../hooks/useFetch.js";
|
import {useFetch} from "../../../hooks/useFetch.js";
|
||||||
import {useEffect, useReducer, useState} from "react";
|
import {useEffect, useReducer, useState} from "react";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faEye, faPen} from "@fortawesome/free-solid-svg-icons";
|
import {faEye, faFilePdf, faPen} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {AxiosError} from "../../../components/AxiosError.jsx";
|
import {AxiosError} from "../../../components/AxiosError.jsx";
|
||||||
import {apiAxios, errFormater, getSaison} from "../../../utils/Tools.js";
|
import {apiAxios, errFormater, getSaison} from "../../../utils/Tools.js";
|
||||||
import {toast} from "react-toastify";
|
import {toast} from "react-toastify";
|
||||||
import {SimpleReducer} from "../../../utils/SimpleReducer.jsx";
|
import {SimpleReducer} from "../../../utils/SimpleReducer.jsx";
|
||||||
import {useNavigate} from "react-router-dom";
|
import {useNavigate} from "react-router-dom";
|
||||||
|
|
||||||
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
|
|
||||||
export function AffiliationCard({clubData}) {
|
export function AffiliationCard({clubData}) {
|
||||||
const setLoading = useLoadingSwitcher()
|
const setLoading = useLoadingSwitcher()
|
||||||
const {data, error} = useFetch(`/affiliation/${clubData.id}`, setLoading, 1)
|
const {data, error} = useFetch(`/affiliation/${clubData.id}`, setLoading, 1)
|
||||||
@ -49,6 +51,13 @@ export function AffiliationCard({clubData}) {
|
|||||||
})}
|
})}
|
||||||
{error && <AxiosError error={error}/>}
|
{error && <AxiosError error={error}/>}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<a href={`${vite_url}/api/club/${clubData.id}/affiliation`} target='#'>
|
||||||
|
<button className="btn btn-primary" type="button" id="button-addon1" style={{marginTop: '1em'}}
|
||||||
|
onClick={e => null}>
|
||||||
|
Téléchargée l'attestation d'affiliation <FontAwesomeIcon icon={faFilePdf}></FontAwesomeIcon>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="modal fade" id="AffiliationModal" tabIndex="-1" aria-labelledby="AffiliationModalLabel"
|
<div className="modal fade" id="AffiliationModal" tabIndex="-1" aria-labelledby="AffiliationModalLabel"
|
||||||
@ -153,4 +162,4 @@ function ModalContent({affiliation, dispatch}) {
|
|||||||
onClick={() => removeAffiliation(affiliation.id, dispatch)}>Supprimer</button>}
|
onClick={() => removeAffiliation(affiliation.id, dispatch)}>Supprimer</button>}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,13 +2,15 @@ import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
|||||||
import {useFetch} from "../../../hooks/useFetch.js";
|
import {useFetch} from "../../../hooks/useFetch.js";
|
||||||
import {useEffect, useReducer, useState} from "react";
|
import {useEffect, useReducer, useState} from "react";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faEye, faPen} from "@fortawesome/free-solid-svg-icons";
|
import {faEye, faFilePdf, faPen} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {AxiosError} from "../../../components/AxiosError.jsx";
|
import {AxiosError} from "../../../components/AxiosError.jsx";
|
||||||
import {apiAxios, getSaison} from "../../../utils/Tools.js";
|
import {apiAxios, getSaison} from "../../../utils/Tools.js";
|
||||||
import {toast} from "react-toastify";
|
import {toast} from "react-toastify";
|
||||||
import {SimpleReducer} from "../../../utils/SimpleReducer.jsx";
|
import {SimpleReducer} from "../../../utils/SimpleReducer.jsx";
|
||||||
import {useNavigate} from "react-router-dom";
|
import {useNavigate} from "react-router-dom";
|
||||||
|
|
||||||
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
|
|
||||||
export function AffiliationCard({clubData}) {
|
export function AffiliationCard({clubData}) {
|
||||||
const setLoading = useLoadingSwitcher()
|
const setLoading = useLoadingSwitcher()
|
||||||
const {data, error} = useFetch(`/affiliation/${clubData.id}`, setLoading, 1)
|
const {data, error} = useFetch(`/affiliation/${clubData.id}`, setLoading, 1)
|
||||||
@ -37,6 +39,13 @@ export function AffiliationCard({clubData}) {
|
|||||||
})}
|
})}
|
||||||
{error && <AxiosError error={error}/>}
|
{error && <AxiosError error={error}/>}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<a href={`${vite_url}/api/club/me/affiliation`} target='#'>
|
||||||
|
<button className="btn btn-primary" type="button" id="button-addon1" style={{marginTop: '1em'}}
|
||||||
|
onClick={e => null}>
|
||||||
|
Téléchargée l'attestation d'affiliation <FontAwesomeIcon icon={faFilePdf}></FontAwesomeIcon>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="modal fade" id="AffiliationModal" tabIndex="-1" aria-labelledby="AffiliationModalLabel"
|
<div className="modal fade" id="AffiliationModal" tabIndex="-1" aria-labelledby="AffiliationModalLabel"
|
||||||
@ -166,4 +175,4 @@ function ModalContent2({clubData, data}) {
|
|||||||
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal">Suivant</button>
|
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal">Suivant</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user