Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom footer and header #387

Open
MassoudSharifi opened this issue Jun 15, 2021 · 18 comments
Open

Custom footer and header #387

MassoudSharifi opened this issue Jun 15, 2021 · 18 comments

Comments

@MassoudSharifi
Copy link

I want to print a list of products. I want to print the date and name of the company on the footer of each page and remove the existing footer and Header

@MatthewHerbst
Copy link
Owner

Hello. What is your question or problem you are having with the library?

@MassoudSharifi
Copy link
Author

MassoudSharifi commented Jun 16, 2021

jurnal

@MatthewHerbst How to edit the ringed places in the image

@MatthewHerbst
Copy link
Owner

Did you modify the pageStyle prop? The default value has a style that removes the header/footer:

pageStyle: "@page { size: auto;  margin: 0mm; } @media print { body { -webkit-print-color-adjust: exact; } }", // remove date/time from top

@MassoudSharifi
Copy link
Author

@MatthewHerbst I remove the default footer and header but I cannot add a custom footer for multiple pages

@MatthewHerbst
Copy link
Owner

Can you please share the code you are using? Very difficult for me to help otherwise

@MassoudSharifi
Copy link
Author

MassoudSharifi commented Jun 22, 2021

@MatthewHerbst Yes, the code is here. and also want to add page numbers in the footer of each page
https://codesandbox.io/s/awesome-brook-0bhtg?file=/src/example/index.js:107-112

@MassoudSharifi
Copy link
Author

@MatthewHerbst Can you help me with how to solve this problem ????

@MatthewHerbst
Copy link
Owner

Apologies, I was traveling and not working.

It does seem there is a bug with pageStyle, I'm looking into that now. You don't have to use pageStyle, and I actually plan to remove it in the next major release. Just use @media print { ... } in your existing CSS and set styles there.

In terms of creating a footer/header, there are two options beyond what you are trying to do. The first is to use a table (example: https://plnkr.co/edit/lWk6Yd?preview). The second is to use the new CSS 3 Page Margin Boxes (see spec + examples here: https://www.w3.org/TR/css-page-3/#margin-boxes)

I will update once I know more about the bug, it's very strange

@MassoudSharifi
Copy link
Author

@MatthewHerbst how to use CSS 3 Page Margin Boxes. is there a library to install? because nothing working without page size property in CSS

@MatthewHerbst
Copy link
Owner

I'm honestly not sure, I had never heard of them until I was researching your problem. Asking on StackOverflow might be best for that. Apologies for the delayed response, I have had to focus on work the last two weeks. I hope to be able to get some time in for this library this coming weekend.

@clydebaron2000
Copy link

A follow up on page margin boxes support?

@Dmitriy-Krivetsky
Copy link

Dmitriy-Krivetsky commented Oct 26, 2021

Maybe it will help someone
Through this function, I was able to make a watermark on all pages, as well as the header and the footer.

 const handlePrint = useReactToPrint({
   content: () => {
      const tableStat = tableStatRef.current.cloneNode(true);
      const PrintElem = document.createElement('div');
      const header = 
        `<img src="${logo}" alt="" class="watermark"/>` + 
        `<div class="page-footer">I'm The Footer</div>`;
      PrintElem.innerHTML = header;
      PrintElem.appendChild(tableStat);
      return PrintElem;
    },
});
@media print {
  .watermark {
    position: fixed;
    top: 50vh;
    z-index: 9;
    width: 50vw;
    page-break-after: always;
    left: 50%;
    transform: translate(-50%, -50%);
    opacity: .1;
  }
  div.page-footer {
    position: fixed;
    z-index: 9;
    bottom: 0;
    width: 100%;
    height: 50px;
    font-size: 15px;
    color: #fff;
    background: red;
    opacity: 0.5;
    page-break-after: always;
  }
}

for me, the key values were:

page-break-after: always;
z-index: 9;

#323 (comment)

@sadidul012
Copy link

sadidul012 commented Feb 18, 2022

This code worked for me with a watermark, sticky footer, header for each page

HTML

            <img alt={""} src={logo} className={"watermark"}/>
            <table className="report-container">
                <thead className="report-header">
                <tr>
                    <th className="report-header-cell">
                        <div className="header-info">
                            header content....
                        </div>
                    </th>
                </tr>
                </thead>
                <tfoot className="report-footer">
                <tr>
                    <td className="report-footer-cell">
                        <div className="footer-info">
                            <div className={"page-footer"}>
                                footer content....
                            </div>
                        </div>
                    </td>
                </tr>
                </tfoot>
                <tbody className="report-content">
                <tr>
                    <td className="report-content-cell">
                        <div className="main">
                            body content...
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>

CSS

div.page-footer {
    text-align: center;
    height: 50px;
    font-size: 10px;
    opacity: 0.8;
    position: fixed;
    bottom: 0;
    width: 100%;
}

div.page-footer p {
    margin: 0;
}

.watermark {
    display: none;
    top: 50vh;
    z-index: -9;
    width: 50vw;
    page-break-after: always;
    left: 50%;
    transform: translate(-50%, -50%);
    opacity: .1;
}

table.report-container {
    page-break-after: always;
    width: 100%;
}

thead.report-header {
    display: table-header-group;
}

tfoot.report-footer {
    display: table-footer-group;
}

div.footer-info, div.page-footer {
    display: none;
    height: 60px;
}


@media print {
    @page {
        size: A4;
        margin: 16mm 16mm 16mm 16mm;
    }

    .watermark {
        display: block;
        counter-increment: page;
        position: fixed;
    }

    div.page-footer, div.footer-info {
        display: block;
    }
}

@prakashmahto01
Copy link

how to remove header/footer in first page ???

@hempun10
Copy link

how to remove header/footer in first page ???

Hey @prakashmahto01 Have you found any solution?

@HarveyNek
Copy link

HarveyNek commented Apr 25, 2024

Hello! In the provided example (CodeSandBox) which i use with functional component i want to print a specific header to every page that is printed..also this header i want to be hidden from user and be shown only in the printed sheet. In other words, i want to print in every page a title "This was printed by user XX in DATE". I tried many ways to resolve this, but i printed the header only in the first page. So it would be helpful if anyone has the idea of how to achieve that.

@bloodykheeng
Copy link

follow this approach it may be of help i also found it online

https://medium.com/@Idan_Co/the-ultimate-print-html-template-with-header-footer-568f415f6d2a

but incase blog is deleted here is its content

ULTIMATE METHOD
So by merging the two methods I can create a template that will meet my requirements.

The general strategy is to use the table method to create empty “place-holders” that will prevent the content from overlapping the header/footer. We’re keeping it empty so that on the last page nothing will be shown. Then, we can use the fixed-position method to place the actual header/footer inside the empty place-holders:

// HTML 
<table>
  <thead><tr><td>
    <div class="header-space">&nbsp;</div>
  </td></tr></thead>
  <tbody><tr><td>
    <div class="content">...</div>
  </td></tr></tbody>
  <tfoot><tr><td>
    <div class="footer-space">&nbsp;</div>
  </td></tr></tfoot>
</table>
<div class="header">...</div>
<div class="footer">...</div>
// CSS
.header, .header-space,
.footer, .footer-space {
  height: 100px;
}
.header {
  position: fixed;
  top: 0;
}
.footer {
  position: fixed;
  bottom: 0;
}

@bloodykheeng
Copy link

in case some one is facing this issue understand this blog then see how i used its logic to implement it in react
[https://medium.com/@Idan_Co/the-ultimate-print-html-template-with-header-footer-568f415f6d2a](url)

its easy you just have to play with tables
like how u see here i create a class that will reserve space at the top when i use position fixed on the header and footer

 <thead>
                <tr>
                    <td>
                        <div className="header-space">&nbsp;</div>
                    </td>
                </tr>
            </thead>

also i reserve footer space

<tfoot>
                <tr>
                    <td>
                        <div className="footer-space">&nbsp;</div>
                    </td>
                </tr>
            </tfoot>

Then i go ahead and style this with postion fixed top 0 and botton 0 respectievely
That will enable them apear on every page but since i reserved space for them they will be in that space

  <div className="header" style={styles.header}>
                <img src="assets/oag_photos/uganda_oag.png" alt="Logo" style={{ height: "50px" }} />
                <span style={styles.title}>{title}</span>
            </div>
            <div className="footer" style={styles.footer}>
                <div>
                    Generated by {loggedInUserDetails?.name} on {moment().format("dddd, MMMM Do YYYY, h:mm:ss A")}
                </div>
                <div>© {new Date().getFullYear()} OAG Citizen Feedback Platform</div>
            </div>
        </table>

here is my report template component

import React from "react";
import moment from "moment";

//
import useAuthContext from "../../../context/AuthContext";

import FeedbackBySectorBarChart from "./charts/FeedbackBySectorBarChart"; // Import Bar Chart
import FeedbackByRegionPieChart from "./charts/FeedbackByRegionPieChart"; // Import Pie Chart
import FeedbackByLocationScopeDoughnutChart from "./charts/FeedbackByLocationScopeDoughnutChart";
import FeedbackByRegionTable from "./tables/FeedbackByRegionTable";
import FeedbackBySectorTable from "./tables/FeedbackBySectorTable";
import FeedbackByLocationScopeTable from "./tables/FeedbackByLocationScopeTable";

const MuiTablePdfTemplate = ({ title = "", columns, data, tableData }) => {
    console.log("🚀 ~ MuiTablePdfTemplate ~ data:", data);
    console.log("🚀 ~ MuiTablePdfTemplate ~ columns:", columns);

    //
    const { getUserQuery } = useAuthContext();

    const loggedInUserDetails = getUserQuery?.data?.data;

    const styles = {
        container: {
            padding: "20px",
            fontFamily: "Arial, sans-serif",
            fontSize: "12px",
        },
        header: {
            display: "flex",
            alignItems: "center",
            justifyContent: "center",
            marginBottom: "20px",
            flexDirection: "column",
            width: "100%",
        },
        logo: {
            height: "50px",
        },
        title: {
            fontSize: "18px",
            fontWeight: "bold",
        },
        paragraphContainer: {
            marginTop: "20px",
        },
        paragraph: {
            marginBottom: "10px",
            lineHeight: "1.6",
        },
        noDataMessage: {
            fontSize: "14px",
            color: "#888",
            textAlign: "center",
            marginTop: "20px",
        },
        footer: {
            marginTop: "20px",
            textAlign: "center",
            fontSize: "10px",
            color: "#666",
        },
        tablesContainer: {
            display: "flex",
            justifyContent: "space-between",
            flexWrap: "wrap",
            marginTop: "20px",
            gap: "20px",
        },
        table: {
            width: "100%", // Adjust width for responsive design
        },

        chartsContainer: {
            display: "flex",
            flexDirection: "column",
            marginTop: "20px",
            gap: "20px",
        },
        chart: {
            width: "100%", // Adjust width for responsive design
        },
        statsContainer: {
            marginTop: "20px",
            display: "flex",
            justifyContent: "space-between",
            gap: "20px",
            fontSize: "14px",
            fontWeight: "bold",
            border: "0.2px solid #ddd",
            padding: "10px",
            borderRadius: "5px",
        },
    };

    // Helper function to dynamically access nested properties
    function getFlatPathValue(obj, path) {
        return obj[path];
    }

    // Helper function to format date values
    function formatValue(value) {
        if (!value) return "";
        // Check if the value is a valid date
        if (moment(value, moment.ISO_8601, true).isValid()) {
            return moment(value).format("MMMM Do YYYY, h:mm a"); // Customize the format as needed
        }
        return value;
    }

    // Calculate statistics
    const totalFeedback = data.length;
    // Use a Set to find unique region names, accessing the "region.name" key
    const uniqueRegions = new Set(
        data.map((row) => row["region.name"]).filter((name) => name) // Filter out undefined or null
    ).size;

    // Use a Set to find unique sector names, accessing the "sector.name" key
    const uniqueSectors = new Set(
        data.map((row) => row["sector.name"]).filter((name) => name) // Filter out undefined or null
    ).size;

    return (
        <table id="print-content" style={styles.container}>
            <thead>
                <tr>
                    <td>
                        <div className="header-space">&nbsp;</div>
                    </td>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>
                        <div className="content">
                            {/* Statistics Section */}
                            <div style={styles.statsContainer}>
                                <div>Total Feedback: {totalFeedback}</div>
                                <div>Number of Regions: {uniqueRegions}</div>
                                <div>Number of Sectors: {uniqueSectors}</div>
                            </div>

                            {/* Tables Section */}
                            <div style={styles.tablesContainer}>
                                <div style={styles.table}>
                                    <FeedbackByLocationScopeTable data={data} />
                                </div>
                                <div style={styles.table}>
                                    <FeedbackByRegionTable data={data} />
                                </div>
                                <div style={styles.table}>
                                    <FeedbackBySectorTable data={data} />
                                </div>
                            </div>
                            {/* Paragraph Section or No Data Message */}
                            <div style={styles.paragraphContainer}>
                                {data.length > 0 ? (
                                    data.map((row, rowIndex) => (
                                        <p key={rowIndex} style={styles.paragraph}>
                                            {columns
                                                .map((col) => {
                                                    const value = getFlatPathValue(row, col.field); // Safely access nested property
                                                    return value ? `${col.title}: ${formatValue(value)}.` : "";
                                                })
                                                .join(" ")}
                                        </p>
                                    ))
                                ) : (
                                    <div style={styles.noDataMessage}>No data available</div>
                                )}
                            </div>

                            {/* Charts Section */}
                            <div style={styles.chartsContainer}>
                                <div style={styles.chart}>
                                    <FeedbackByLocationScopeDoughnutChart data={data} />
                                </div>
                                <div style={styles.chart}>
                                    <FeedbackByRegionPieChart data={data} />
                                </div>
                                <div style={styles.chart}>
                                    <FeedbackBySectorBarChart data={data} />
                                </div>
                            </div>
                        </div>
                    </td>
                </tr>
            </tbody>
            <tfoot>
                <tr>
                    <td>
                        <div className="footer-space">&nbsp;</div>
                    </td>
                </tr>
            </tfoot>
            <div className="header" style={styles.header}>
                <img src="assets/oag_photos/uganda_oag.png" alt="Logo" style={{ height: "50px" }} />
                <span style={styles.title}>{title}</span>
            </div>
            <div className="footer" style={styles.footer}>
                <div>
                    Generated by {loggedInUserDetails?.name} on {moment().format("dddd, MMMM Do YYYY, h:mm:ss A")}
                </div>
                <div>© {new Date().getFullYear()} OAG Citizen Feedback Platform</div>
            </div>
        </table>
    );
};

export default MuiTablePdfTemplate;

here is my parent component where i print from


const printDocument = useReactToPrint({
        contentRef: pdfReportRef,
        documentTitle: printConfig?.title || "Citizen Feedback Plaform Report", // Fall back to "Default Document Title" if title is undefined
        // pageStyle: `
        //     @page { size: A4 ${printConfig?.orientation}; margin: 1cm; }
        //     @media print {
        //       body { -webkit-print-color-adjust: exact; }
        //       div#print-content { height: 100%; }
        //       .header, .footer { background-color: transparent !important; }
        //       img { display: block; margin: 0 auto; }
        //     }
        //   `,
        pageStyle: `
        @page {
            size: A4 ${printConfig?.orientation || "portrait"};
            margin: 1cm;
        }
        @media print {
            body {
                -webkit-print-color-adjust: exact;
                margin: 0;
            }
        
            /* Reserved Spaces */
            .header-space,
            .footer-space {
                height: 100px;
            }
        
            /* Fixed Header */
            .header {
                position: fixed;
                top: 0;
                width: 100%;
                background-color: white !important;
                text-align: center;
                z-index: 10;
                border-bottom: 1px solid #ddd;
            }
        
            /* Fixed Footer */
            .footer {
                position: fixed;
                bottom: 0;
                width: 100%;
                background-color: white !important;
                text-align: center;
                font-size: 10px;
                color: #666;
                z-index: 10;
                border-top: 1px solid #ddd;
            }
        
            /* Content Space Adjustments */
            .content {
                margin-top: -50px; /* Match header-space height */
                margin-bottom: 100px; /* Match footer-space height */
            }
        }
        `,
        onAfterPrint: () => {
            setPrintConfig(null); // Reset printConfig to null after printing
        },
        onBeforeGetContent: () => {
            if (!window.matchMedia("print").matches) {
                // Fallback: Save as PDF if the print dialog cannot be triggered
                saveAsPDF();
            }
        },
        onPrintError: (error) => {
            console.error("Print Error:", error);
            saveAsPDF();
            setPrintConfig(null); // Reset printConfig on error
        },
    });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants