import React, { useState, useRef } from 'react';
import Modal from 'react-modal';
import './App.css';
function App() {
const [scenarios, setScenarios] = useState([]);
const [images, setImages] = useState([]);
const [editing, setEditing] = useState(null);
const [modalIsOpen, setModalIsOpen] = useState(false);
const [title, setTitle] = useState('Titulo: ');
const [given, setGiven] = useState('Dado que ');
const [when, setWhen] = useState('Quando ');
const [and, setAnd] = useState('E ');
const [then, setThen] = useState('Então ');
const inputRef = useRef([]);
const openModal = () => {
setTitle(`CT${scenarios.length + 1}: `);
setModalIsOpen(true);
};
const closeModal = () => {
setModalIsOpen(false);
};
const handleImageUpload = (e) => {
const files = Array.from(e.target.files);
const newImages = files.map(file => URL.createObjectURL(file));
setImages(oldImages => [...oldImages, ...newImages]);
};
const handleAddScenario = () => {
if (given.trim() === '' || when.trim() === '' || and.trim() === '' || then.trim() === '') {
alert('Please fill in all the textareas.');
return;
}
const scenario = {
title,
given,
when,
and,
then,
images,
};
if (editing !== null) {
const newScenarios = [...scenarios];
newScenarios[editing] = scenario;
setScenarios(newScenarios);
setEditing(null);
} else {
setScenarios(oldScenarios => [...oldScenarios, scenario]);
setTitle(`CT ${scenarios.length + 2}: `);
}
setGiven('Dado que ');
setWhen('Quando ');
setAnd('E ');
setThen('Então ');
setImages([]);
setModalIsOpen(false);
};
const handleTextChange = (e, index, field) => {
const text = e.target.value;
const newScenarios = [...scenarios];
if (index >= 0 && index < newScenarios.length) {
newScenarios[index][field] = text;
setScenarios(newScenarios);
} else {
console.error(`Invalid index: ${index}`);
}
};
const handleRemoveImage = (scenarioIndex, imageIndex) => {
const newScenarios = [...scenarios];
newScenarios[scenarioIndex].images.splice(imageIndex, 1);
setScenarios(newScenarios);
};
const handleImageChange = (e, scenarioIndex, imageIndex) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onloadend = () => {
const newScenarios = [...scenarios];
newScenarios[scenarioIndex].images[imageIndex] = reader.result;
setScenarios(newScenarios);
};
reader.readAsDataURL(file);
};
const handleRemoveScenario = (index) => {
const newScenarios = [...scenarios];
newScenarios.splice(index, 1);
setScenarios(newScenarios);
};
return (
<div className="App">
<Modal
isOpen={modalIsOpen}
onRequestClose={closeModal}
contentLabel="Create Scenario"
>
<textarea
value={title}
onChange={e => setTitle(e.target.value)}
/>
<textarea
value={given}
onChange={e => setGiven(e.target.value)}
/>
<textarea
value={when}
onChange={e => setWhen(e.target.value)}
/>
<textarea
value={and}
onChange={e => setAnd(e.target.value)}
/>
<textarea
value={then}
onChange={e => setThen(e.target.value)}
/>
<label htmlFor="fileUpload" className="fileUpload"> Importar Imagens
<input id="fileUpload" type="file" onChange={handleImageUpload} multiple />
</label>
<button onClick={handleAddScenario}><i class="fas fa-plus"></i>Add Scenario</button>
<button onClick={closeModal}><i class="fas fa-times"></i>Close</button>
</Modal>
{scenarios.map((scenario, index) => (
<div key={index}>
<div className = "hover-container">
<div className="hover-button">
<button onClick={() => handleRemoveScenario(index)}>Remove Scenario {index+1}</button>
</div>
<textarea
value={scenario.title}
onChange={e => handleTextChange(e, index, 'title')}
style={{
width: '100%',
minHeight: '5px',
border: '1px solid #ccc',
borderRadius: '4px',
padding: '10px'
}}
/>
<textarea
value={scenario.given}
onChange={e => handleTextChange(e, index, 'given')}
style={{
width: '100%',
minHeight: '5px',
border: '1px solid #ccc',
borderRadius: '4px',
padding: '10px'
}}
/>
<textarea
value={scenario.when}
onChange={e => handleTextChange(e, index, 'when')}
style={{
width: '100%',
minHeight: '5px',
border: '1px solid #ccc',
borderRadius: '4px',
padding: '10px'
}}
/>
<textarea
value={scenario.and}
onChange={e => handleTextChange(e, index, 'and')}
style={{
width: '100%',
minHeight: '5px',
border: '1px solid #ccc',
borderRadius: '4px',
padding: '10px'
}}
/>
<textarea
value={scenario.then}
onChange={e => handleTextChange(e, index, 'then')}
style={{
width: '100%',
minHeight: '5px',
border: '1px solid #ccc',
borderRadius: '4px',
padding: '10px'
}}
/>
</div>
{scenario.images.map((image, imageIndex) => (
<div key={imageIndex} className="image-container">
<img src={image} alt="" />
<div className="image-buttons">
<button onClick={() => handleRemoveImage(index, imageIndex)}>
<i className="fa fa-trash"></i>
</button>
<input type="file" style={{display: 'none'}} ref={input => inputRef.current[imageIndex] = input} onChange={e => handleImageChange(e, index, imageIndex)} />
<button onClick={() => inputRef.current[imageIndex].click()}>
<i className="fa fa-pencil-alt"></i>
</button>
</div>
</div>
))}
</div>
))}
<button onClick={openModal}>Create Scenario</button>
</div>
);
}
export default App;
This code, creates scenarios, where you need to fill all the necessary information and add images(if you want too), and then add this informations to the page as an div, you can create multiple of then, remove images, change undesired images, etc.
Given this code, I want to create a button that converts the entire page into a dynamic pdf, with all the scenarios and images displayed, remove the buttons and turn the textareas in to normal text when printed.
You can use any mehtod that acheives that result, i’ve tried jspdf, html2canvas and even file-saver, but they didn’t work all that well.
I feel like i tried everthing but nothing worked. Either they didn’t show anything in the pdf or add multiple not needed pages, wrong placement of the elements in the pdf, an a lot of other problems.
How can I make something like that?
Your question is off-topic on Stack Overflow, for a few reasons. From description, your issues seem to be CSS related, but you haven’t added the CSS. Ideally you should provide a runnable minimal reproducible example using either a Stack snippet or codesandbox.io (or similar), if you need a node-like environment. Secondly, even if you did add all the necessary details for someone to repro, and the method which got you closest to your goal into the question, this is a task which would take a seasoned dev days to finalise. A SO question should not require more than 15 minutes for an answer. That’s the recommended size.