Shared posts

01 Apr 20:41

Apple Removes iPhone Vibe Coding App from App Store

23 Jan 21:36

Zijn blessure lijkt hem niet te deren: Wout van Aert deelt geruststellende trainingsbeelden uit Spanje

by Redactie
Het lijkt goed te gaan met de enkelblessure van Wout van Aert (31). De renner is momenteel op stage met zijn ploeg Team Visma Lease-a-Bike. Vanop trainingskamp deelt de renner enkele geruststellende sfeerbeelden.
12 Dec 18:08

Cassius the giant crocodile died from sepsis after 40-year-old dormant infection burst from 'abscess,' necropsy reveals

by Sascha Pare
Cassius was an 18-foot-long saltwater crocodile living in captivity in Marineland Crocodile Park in Australia. He died last year at the age of about 120, and we finally know why.
13 Oct 02:57

Djokovic to meet 204th-ranked Vacherot in Shanghai semis

Novak Djokovic said Thursday he was "concerned" about his physical state, but still dispatched Belgium's Zizou Bergs 6-3, 7-5 to set up a Shanghai Masters semifinal against tournament…
18 Dec 13:40

How to Build a Custom MedusaJS Admin Dashboard with NextJS, Supabase, and Tailwind CSS

by Victor Yakubu

When it comes to managing an e-commerce business, no two business needs are the same. For example, one e-commerce store might need a dashboard focused on tracking real-time inventory, while another prioritizes visualizing sales trends and customer engagement. That’s why off-the-shelf admin dashboards or pre-built templates frequently fall short: they’re not designed to accommodate the specific needs of individual businesses.

However, a customizable solution like Medusa solves this by providing the building blocks and REST API endpoints you need to build custom e-commerce solutions that can adapt to the business’s current needs. This ensures you retain control over every aspect of your e-commerce ecosystem.

In this article, you’ll learn how to:

  • Set up an e-commerce backend using MedusaJS and Supabase.

  • Build a custom sales dashboard using Next.js and Tailwind CSS.

  • Extend MedusaJS’s admin functionality to meet specific business requirements.

By the end of this guide, you’ll have an admin dashboard that looks like this:

Custom Admin Dashboard with MedusaJS

Prerequisites

To follow along with this tutorial, you need the following:

  1. Basic knowledge of Next.js and PostgreSQL

  2. Node.js (v16 or later) — Install it from nodejs.org.

Setting up your Medusa store locally

Follow these steps to set up your Medusa store:

Step 1: Create a PostgreSQL database using Supabase

  • Visit Supabase and sign up.

  • Click “Start your project” and create a new account.

  • From your dashboard, click “New Project”

  • Fill in the details for your database, i.e., Project Name and Database password. You can either use the default region or select one closer to you.

  • After that, click “ Create new project”. It will create a new Supabase project with an entire Postgres database.

Supabase

Step 2: Get your database connection string

  • Navigate to Project Settings > Connect.

Supabase Sign up Dashboard

  • You will find the connection string under Connection String > URL. Save it — you’ll need it for the Medusa setup.

Supabase Dashboard

Step 3: Install and set up Medusa locally

Run the following command to install and set up your Medusa project, connecting it to your Supabase database:

npx create-medusa-app@latest - seed - db-url postgresql://postgres:<password>@<host>.supabase.co:5432/postgres

Replace <password> in the command with the database password you created in Step 1.

Command flags explained:

  • — seed: Seeds the database with demo data.

  • — db-url: Specifies your database connection URL.

You can see the other CLI options that create-medusa-app accepts in the documentation.

Once the installation has been completed, your project will be served in the following ports:

  1. Medusa backend: http://localhost:9000

  2. Medusa admin dashboard: http://localhost:7001

supabase

To access the admin dashboard, create an admin account or use the default credentials ( Email: admin@medusa-test.com Password: supersecret)

Supabase

Add a custom Sales Overview page.

Aside from the default features, MedusaJS allows you to add custom routes to your admin dashboard, enabling you to track specific business metrics like sales performance. Here’s how you can add a custom Sales Overview page:

1. Creating a custom Admin UI route

MedusaJS admin routes are React components in the src/admin/routes directory. To create a custom route for sales:

  1. Navigate to src/admin/routes in your project directory.

  2. Create a folder structure: sales/page.tsx.

    └── my-store/
    └── src/
        └── admin/
            └── routes/
                └── sales/
                    └── page.tsx
    
  3. Inside page.tsx, export a React component for the sales page:

const Sales = () => {
  return (
    <div>
      See the number of products sold and the number remaining in stock
    </div>
  )
}

export default Sales

Visit http://localhost:7001/a/sales to see your custom page. However, it is not accessible from the sidebar; you will fix that in the next section.

MedusaJS

2. Adding the route to the Sidebar

To make the route accessible from the sidebar:

  • Import RouteConfig from @medusajs/admin and define the route configuration:
import { RouteConfig } from "@medusajs/admin"
import { CircleStack } from '@medusajs/icons';

const Sales = () => {
  return (
    <div>
      See the number of product sold and the number left in stock
    </div>
  )
}

export const config: RouteConfig = {
  link: {
    label: "Sales",
    icon: CircleStack,
  },
}

export default Sales

The Sales route will now appear on the admin dashboard sidebar.

Sales route

Extending your Product Entity to add custom fields

To make the Sales Overview page functional, you need to ensure your database supports all the required data. Specifically, you’ll want to display each product’s current stock, price, and units sold. While the first two are available in the default schema, the units_sold data isn’t. So, how do you add custom fields?

Step 1: Reviewing the current schema

Start by exploring the existing data structure. You can make a GET request to the /admin/products endpoint to see the available fields for each product. This endpoint is accessible at:

http://localhost:9000/admin/products

You’ll notice there isn’t a column for units_sold. This is where customization becomes necessary.

Step 2: Modifying your database schema to the units_sold Column

Since Medusa uses a PostgreSQL database with TypeORM as the ORM, you’ll need to update both the database schema and the MedusaJS product entity model to add new columns.

To start, modify your database schema to include a units_sold column. For better granularity, you can add quarterly columns (units_sold_q1, units_sold_q2, etc.). Run the following SQL commands directly on your database:

ALTER TABLE product ADD COLUMN units_sold_q1 INT DEFAULT 0;
ALTER TABLE product ADD COLUMN units_sold_q2 INT DEFAULT 0;
ALTER TABLE product ADD COLUMN units_sold_q3 INT DEFAULT 0;
ALTER TABLE product ADD COLUMN units_sold_q4 INT DEFAULT 0;

💡 After running these commands, the new columns will exist in your database but won’t yet appear in API responses or the admin dashboard. To fix this, you’ll need to update the MedusaJS entity model.

Step 3: Extending the Product Entity Model

Medusa uses TypeORM to define its models. To add the new columns to the product entity, create a file named product.ts in ./src/models/:

import { Column, Entity } from "typeorm";
import { Product as MedusaProduct } from "@medusajs/medusa";
@Entity()
export class Product extends MedusaProduct {
@Column({ default: 0 })
units_sold_q1: number;
@Column({ default: 0 })
units_sold_q2: number;
@Column({ default: 0 })
units_sold_q3: number;
@Column({ default: 0 })
units_sold_q4: number;
}

The @Column decorator ensures that TypeORM maps the fields to your database.

Step 4: Updating type definitions

If you’re using TypeScript, add the new columns to your IDE’s autocomplete by extending Medusa’s core Product interface. Create a file at src/index.d.ts:

export declare module "@medusajs/medusa/dist/models/product" {
declare interface Product {
units_sold_q1: number;
units_sold_q2: number;
units_sold_q3: number;
units_sold_q4: number;
 }
}

Step 5: Exposing new fields in the API

Medusa’s API won’t return your custom columns by default. To include these fields, you need to modify the products endpoint configuration. Create a file at src/loaders/extend-product-fields.ts:

export default async function () {
const imports = await import(
"@medusajs/medusa/dist/api/routes/admin/products/index"
) as any;
imports.defaultAdminProductFields = [
…imports.defaultAdminProductFields,
"units_sold_q1",
"units_sold_q2",
"units_sold_q3",
"units_sold_q4",
 ];
};

Then, register this loader in your Medusa project by adding it to the loaders array in medusa-config.js.

Step 6: Creating a TypeORM data source

To synchronize your new fields with the database, create a TypeORM data source file at the root of your Medusa project. Save it as datasource.js:

const { DataSource } = require("typeorm");
const AppDataSource = new DataSource({
type: "postgres",
port: 5432,
username: "<YOUR_DB_USERNAME>",
password: "<YOUR_DB_PASSWORD>",
database: "<YOUR_DB_NAME>",
entities: ["dist/models/*.js"],
migrations: ["dist/migrations/*.js"],
});
module.exports = {
datasource: AppDataSource,
};

Step 7: Writing and running migrations

To update the database schema programmatically, create and execute a migration:

  • Generate a migration file:
npm run build
npx typeorm migration:create src/migrations/AddUnitsSoldColumnToProduct
  • Edit the generated migration file:
import { MigrationInterface, QueryRunner } from "typeorm";

export class AddUnitsSoldColumnToProduct implements MigrationInterface {
  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`
      ALTER TABLE product ADD COLUMN units_sold_q1 INT DEFAULT 0;
      ALTER TABLE product ADD COLUMN units_sold_q2 INT DEFAULT 0;
      ALTER TABLE product ADD COLUMN units_sold_q3 INT DEFAULT 0;
      ALTER TABLE product ADD COLUMN units_sold_q4 INT DEFAULT 0;
    `);
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`
      ALTER TABLE product DROP COLUMN units_sold_q1;
      ALTER TABLE product DROP COLUMN units_sold_q2;
      ALTER TABLE product DROP COLUMN units_sold_q3;
      ALTER TABLE product DROP COLUMN units_sold_q4;
    `);
  }
}
  • Run the migration:
npm run build
npx medusa migrations run

Step 8: Verifying your changes

After running the migration, your database schema will include the new columns, and they’ll be visible in your /admin/products API responses. Test these changes by sending a GET request to the endpoint.

Now, your MedusaJS project is equipped to track and display quarterly sales data, enabling you to create a fully functional Sales Overview page tailored to your needs.

Building the Sales Page UI

Now that the data flows seamlessly between your admin dashboard and the Medusa backend, it’s time to implement the code for your Admin UI. This section will guide you through building the user interface for a Sales Overview Page.

MedusaJS comes bundled with Medusa UI, a React-based design system that includes components, hooks, utility functions, icons, and pre-made Tailwind CSS classes. These tools ensure a consistent and professional design across your admin dashboard.

Your Sales Overview Page will feature three main components:

  • Header: Displays key metrics like revenue and customer counts.

  • Bar Chart: Visualizes sales data by quarter.

  • Recent Orders Section: Lists recent customer orders.

Admin Dashboard

Creating the Sales Page layout

In the page.tsx file, structure the layout of your Sales Page:

import { RouteConfig } from '@medusajs/admin';
import { CircleStack } from '@medusajs/icons';
import TopCards from './components/TopCards';
import BarChart from './components/BarChart';
import RecentOrders from './components/RecentOrders';
const Sales = () => {
return (
<div>
<TopCards />
<div className="p-4 grid grid-cols-2 gap-4">
<BarChart />
<RecentOrders />
</div>
</div>
);
};
// Adding route into the admin dashboard sidebar
export const config: RouteConfig = {
link: {
label: 'Sales',
icon: CircleStack,
},
};
<style></style>;
export default Sales;

Next, create a component folder under src/admin/routes/sales; inside the component folder, create the following files: TopCards.tsx, BarChart.tsx and RecentOrders.tsx.

Header section

The Header Section displays sales statistics such as quarterly revenue and total customers. Create this component in TopCards.tsx:

src/admin/routes/sales/components/TopCards.tsx

import { Text } from '@medusajs/ui';
const TopCards = () => {
  return (
    <div className="gap-y-large flex flex-col">
      <div className=" 'p-4 gap-y-2xsmall flex flex-col">
        <h2 className="inter-xlarge-semibold">Sales</h2>
        <Text className="inter-base-regular text-grey-50">
          See the number of product sold and the number remaining in stock
        </Text>
      </div>

      <div className=" flex sm:flex-none gap-4 p-4">
        <div className="lg:col-span-2 col-span-1 bg-white flex justify-between w-full border p-4 rounded-lg cursor-pointer hover:shadow-lg transform hover:scale-[103%] transition duration-300 ease-out border-l-[4px] border-[#1F2937]">
          <div className="flex flex-col w-full pb-4">
            <p className="text-2xl font-bold">$7,845</p>
            <p className="text-gray-600">Quarterly Revenue</p>
          </div>
          <p className="bg-green-200 flex justify-center items-center p-2 rounded-lg">
            <span className="text-green-700 text-lg">+18%</span>
          </p>
        </div>
        <div className="lg:col-span-2 col-span-1 bg-white flex justify-between w-full border p-4 rounded-lg cursor-pointer hover:shadow-lg transform hover:scale-[103%] transition duration-300 ease-out border-l-[4px] border-[#1F2937]">
          <div className="flex flex-col w-full pb-4">
            <p className="text-2xl font-bold">$1,14,783</p>
            <p className="text-gray-600">YTD Revenue</p>
          </div>
          <p className="bg-green-200 flex justify-center items-center p-2 rounded-lg">
            <span className="text-green-700 text-lg">+11%</span>
          </p>
        </div>
        <div className="bg-white flex justify-between w-full border p-4 rounded-lg cursor-pointer hover:shadow-lg transform hover:scale-[103%] transition duration-300 ease-out border-l-[4px] border-[#1F2937]">
          <div className="flex flex-col w-full pb-4">
            <p className="text-2xl font-bold">10,845</p>
            <p className="text-gray-600">Customers</p>
          </div>
          <p className="bg-green-200 flex justify-center items-center p-2 rounded-lg">
            <span className="text-green-700 text-lg">+17%</span>
          </p>
        </div>
      </div>
    </div>
  );
};

export default TopCards;

Bar chart section

The Bar chart visualizes sales revenue by quarter. Create this component in BarChart.tsx:

src/admin/routes/sales/components/BarChart.tsx

import React, { useState, useEffect } from 'react';
import { Bar } from 'react-chartjs-2';
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend,
} from 'chart.js';
ChartJS.register(
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend
);

import {useAdminProducts} from 'medusa-react'

const BarChart = () => {

    const { products} = useAdminProducts();
    const [totalRevenueQ1, setTotalRevenueQ1] = useState(null);
    const [totalRevenueQ2, setTotalRevenueQ2] = useState(null);
    const [totalRevenueQ3, setTotalRevenueQ3] = useState(null);
    const [totalRevenueQ4, setTotalRevenueQ4] = useState(null);

    useEffect(() => {
      async function calculateTotalRevenue() {
        // Check if products is available
        if (!products) {
          console.error('Products data is not available.');
          return;
        }

        // Step 1: Map through Products and calculate revenue for each product, each quarter
        const revenuePerProductQ1 = products.map((product) => {
          const amount = product.variants[0].prices[0].amount;
          const sold_q1 = product.units_sold_q1;
          console.log('amount:', amount, 'sold_q1:', sold_q1);
          return amount * sold_q1;
        });
        const revenuePerProductQ2 = products.map((product) => {
          const amount = product.variants[0].prices[0].amount;
          const sold_q2 = product.units_sold_q2;
          return amount * sold_q2;
        });
        const revenuePerProductQ3 = products.map((product) => {
          const amount = product.variants[0].prices[0].amount;
          const sold_q3 = product.units_sold_q3;
          return amount * sold_q3;
        });
        const revenuePerProductQ4 = products.map((product) => {
          const amount = product.variants[0].prices[0].amount;
          const sold_q4 = product.units_sold_q4;
          return amount * sold_q4;
        });

        // Step 2: Sum the individual multiplication results to get the total revenue for each quarter
        const totalRevenueQ1 = revenuePerProductQ1.reduce(
          (total, revenue) => total + revenue,
          0
        );
        const totalRevenueQ2 = revenuePerProductQ2.reduce(
          (total, revenue) => total + revenue,
          0
        );
        const totalRevenueQ3 = revenuePerProductQ3.reduce(
          (total, revenue) => total + revenue,
          0
        );
        const totalRevenueQ4 = revenuePerProductQ4.reduce(
          (total, revenue) => total + revenue,
          0
        );

        // Set quarterly totalRevenue states with the calculated value
        setTotalRevenueQ1(totalRevenueQ1);
        setTotalRevenueQ2(totalRevenueQ2);
        setTotalRevenueQ3(totalRevenueQ3);
        setTotalRevenueQ4(totalRevenueQ4);
      }
      // calculate total revenue when products data is available
      if (products) {
        calculateTotalRevenue();
      }

    }, [products]);

   const data = {
     labels: ['Q1', 'Q2', 'Q3', 'Q4'],
     datasets: [
       {
         label: 'Total Revenue',
         data: [totalRevenueQ1, totalRevenueQ2, totalRevenueQ3, totalRevenueQ4],
         backgroundColor: '#32de84',
         borderColor: 'rgb(0,128,0)',
       },
     ],
   };

  return (
    <>
      <div className="w-full md:col-span-2 relative lg:h-[70vh] h-[50vh] m-auto p-4 border rounded-lg bg-white">
        <div className="w-100% h-[70vh]">
          <Bar data={data} />
        </div>
      </div>
    </>
  );
};

export default BarChart;
  • The useAdminProducts hook gives you access to the list of available products in the database and also automatically handles auth, passing along credentials in its requests to the Medusa backend as long as you’re signed in to the Admin dashboard. You can learn more about the Admin APIs for managing products here.

Recent Orders section

The Recent Orders Section lists customer orders. Create this component in RecentOrders.tsx:

src/admin/routes/sales/components/RecentOrders.tsx

import { ShoppingBag } from '@medusajs/icons';
import { useAdminOrders } from 'medusa-react';

const RecentOrders = () => {
  const { orders, isLoading } = useAdminOrders();

  return (
    <div className="w-full col-span-1 relative lg:h-[70vh] h-[50vh] m-auto p-4 rounded-lg bg-white overflow-scroll">
      <h1 className="text-center inter-large-semibold text-[#1F2937]">
        Recent Orders
      </h1>
      {isLoading && <span>Loading...</span>}
      {orders && !orders.length && <span>No Orders</span>}
      {orders && orders.length > 0 && (
        <ul>
          {orders.map((order, id) => (
            <li
              key={order.id}
              className="bg-gray-50 hover:bg-gray-100 rounded-lg my-3 p-2 flex items-center cursor-pointer"
            >
              <div className="bg-green-200 rounded-lg p-3">
                <ShoppingBag />
              </div>
              <div className="pl-4">
                <p className="text-gray-800 font-bold">
                  €{order.payments[0].amount}
                </p>
                <p className="text-gray-400 text-sm">
                  {order.customer.first_name}
                </p>
              </div>
              <p className="lg:flex md:hidden absolute right-6 text-sm">
                {order.items[0].title}
              </p>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

export default RecentOrders;
  • The useAdminOrders hook gives you access to the list of orders made by clients, and only authorised credentials have access to this.

  • The useAdminOrders hook also gives you access to all of its properties like first_name, payments_amount, title, etc.

Your Sales Overview Page is now functional and visually appealing with these components. You can modify and add as many functionalities as you want to suit your needs.

Conclusion

This tutorial demonstrated how to build a custom MedusaJS admin dashboard using Next.js, Supabase, and Tailwind CSS. By following these steps, you learned how to set up a Medusa backend, create a dynamic user interface, and extend the Medusa functionalities to build tailored components like the Sales Overview Page.

Check out the Medusa documentation to learn more about advanced features and other customization options you can add to your e-commerce experience.

01 Jan 11:43

Bel­gisch-Marokkaan­se vrouw slachtof­fer van mesaanval, mogelijk terroristische aanslag

by IBB
Een Belgisch-Marokkaanse toeriste is donderdagmiddag het slachtoffer geworden van een mesaanval op het strand van de Marokkaanse badplaats Agadir. Dat melden meerdere buitenlandse media. Opmerkelijk: de dader probeerde na de aanval al zwemmend te ontsnappen.
25 Dec 16:48

Officials urged tough Covid messaging for 'white van man'

Emails published by the Covid inquiry show advisers thought the group were less scared of the virus.
25 Dec 16:25

William Ruto: The ‘tax collector’ president sparking Kenyans' anger

Tax hikes have led to many Kenyans saying their president is a greedy tax collector like Zacchaeus.
25 Dec 16:25

The people having a thrifty, vintage, money-saving Christmas

Vintage and money-saving experts share the easiest ways you can save money at Christmas.
02 Jun 01:58

Care homes in Wales charging top-up fees

Care homes are criticised for charging top-up fees such as taking residents to medical appointments.
06 Apr 00:05

Alright on to ruby and boy oh boy!?!?

by danielarmbruster0314

Today in the curriculum of my bootcamp they were introducing the concept of require and require_relative. However the example they used had classes and so I did some digging and are ruby classes somewhat similar or conceptual close to react components? I mean you build it so that when it is passed data it builds an object and in react you pass a component data to create DOM elements. Is this conclusion accurate?

25 Feb 16:24

Methane Emissions From the Energy Sector Are 70% Higher Than Official Figures: IEA

by BeauHD
New submitter Klaxton shares an excerpt from a new report released today by the International Energy Agency (IEA): Global methane emissions from the energy sector are about 70% greater than the amount national governments have officially reported, according to new IEA analysis released today, underlining the urgent need for enhanced monitoring efforts and stronger policy action to drive down emissions of the potent greenhouse gas. Methane is responsible for around 30% of the rise in global temperatures since the Industrial Revolution, and quick and sustained emission reductions are key to limiting near-term warming and improving air quality. Methane dissipates faster than carbon dioxide (CO2) but is a much more powerful greenhouse gas during its short lifespan, meaning that cutting methane emissions would have a rapid effect on limiting global warming. The energy sector accounts for around 40% of methane emissions from human activity, and this year's expanded edition of the IEA's Global Methane Tracker includes country-by-country emissions from coal mines and bioenergy for the first time, in addition to continued detailed coverage of oil and natural gas operations. Methane emissions from the energy sector grew by just under 5% last year. This did not bring them back to their 2019 levels and slightly lagged the rise in overall energy use, indicating that some efforts to limit emissions may already be paying off. "At today's elevated natural gas prices, nearly all of the methane emissions from oil and gas operations worldwide could be avoided at no net cost," said IEA Executive Director Fatih Birol. "The International Energy Agency has been a longstanding champion of stronger action to cut methane emissions. A vital part of those efforts is transparency on the size and location of the emissions, which is why the massive underreporting revealed by our Global Methane Tracker is so alarming." If all methane leaks from fossil fuel operations in 2021 had been captured and sold, then natural gas markets would have been supplied with an additional 180 billion cubic meters of natural gas. That is equivalent to all the gas used in Europe's power sector and more than enough to ease today's market tightness. The intensity of methane emissions from fossil fuel operations range widely from country to country: the best performing countries and companies are over 100 times better than the worst. Global methane emissions from oil and gas operations would fall by more than 90% if all producing countries matched Norway's emissions intensity, the lowest worldwide.

Read more of this story at Slashdot.

26 Jan 08:19

The Forgotten Women Who Ruled the Medieval Middle East

by Sarah Durn

Damascus wasn’t in good shape in 1132. Assassins had just killed the king. The new king, Isma’il, was paranoid, greedy, and prone to violence—even brutally executing his own half-brother. Then, in a move that violates kingship’s most basic tenets, Isma’il planned to give Damascus away to the enemy. Enter Khatun Zumurrud, Isma'il's mother. She was not about to let that happen. In 1135, she had her own son assassinated and his body dragged through the streets.

Zumurrud wasn’t the only woman you didn’t want to mess with in the medieval Middle East. The Kingdom of Jerusalem, a Christian Crusader State founded by Europeans in 1099, saw two queens during this period, Melisende and her granddaughter Sibylla, whose political machinations, affairs, and rebellions could rival those of Game of Thrones. There was also Theodora of Jerusalem, the widowed queen who ran away to the Islamic world with the man she loved (who also happened to be her father's cousin), Alice of Antioch, who twice tried to take the throne, and more.

Atlas Obscura talked to Katherine Pangonis, whose new book, Queens of Jerusalem: The Women Who Dared to Rule, explores the complicated lives and legacy of the forgotten women who ruled the medieval Middle East.

article-image

What was the role of women in the medieval European world? In the Crusader State of Jerusalem?

At this time, women are very much second-class citizens. They have limited rights in terms of inheritance, owning property, their marriages. Women aren't meant to be in power unless they're protecting their husband's land or his interests. So that's one of the places in which it's acceptable for a woman to have power and that's the same in Europe as it is in the Middle East.

What I found quite interesting was that the unique instability and this constant state of crisis in Outremer [another name for the Crusader States] gave women more opportunities to not just inherit power, but also to wield it.

Does there have to be instability to upend the patriarchal structure?

That's generally a key part of women getting power. Basically, for women to take power, there has to be a shortage of suitable men—either sons aren’t being born or kings and heirs are dying.

So if you look at the example of Matilda of England. She's an English queen who, in theory, would become one of the first queens regnant. Her father, Henry I, has a son and heir, but then his son is killed in the wreck of the White Ship. And he names Matilda heir and she's a direct contemporary of Melisende [of Jerusalem]. So it's a great comparison because Melisende is going through something similar in the Middle East.

The difference is that in the Middle East, there isn't the possibility of having a succession crisis because the Crusader States are so fragile. They're in danger all the time. They can't afford a civil war of the sort that England then has over whether or not Matilde should inherit, a civil war that lasts well over a decade.

article-image

Tell us more about Queen Melisende of Jerusalem. How did she come to power?

Melisende is the daughter of Baldwin II, the third king of Jerusalem. And Baldwin has four daughters and no sons, and he makes it clear from quite early that Melisende’s the heiress to Jerusalem. Then she's married to a suitable man, a guy called Fulk of Anjou. And Fulk is 100% expecting to be named the sole heir to the kingdom. However, on Baldwin's deathbed, he changes the rules. And he says, in fact, I'm leaving my kingdom in three parts to Melisende, Fulk, and their baby son, which is obviously infuriating to Fulk.

Then there's this major scandal that erupts very shortly after they come to power—someone accuses Melisende's cousin, the Count Hugh of Jaffa, of treason and challenges him to trial by combat. There's a rumor that Melisende was having an affair with her cousin. And then this plunges the Kingdom of Jerusalem into what is tantamount to a civil war. Hugh rebels and allies with the Egyptians of Ascalon and tries to fight Fulk and is defeated. After that, Hugh is sentenced to exile. And while he waited to be exiled, there's an assassination attempt made in his life. He gets stabbed very badly and then dies there shortly after.

Melisende at this point loses her temper. She’s just is so furious about Hugh’s death and about being dragged into this scandal that she suddenly really comes into her own and starts asserting her authority. Fulk is genuinely terrified of her. And from that moment forward, Melisende's name is on charters. We start to see her influence in making donations to various causes, such as the military orders, the Church, and then—Fulk dies! This leaves Melisende as regent for her son because he’s too young to rule.

And what's even more remarkable is that even once her son comes of age, Melisende keeps ruling and doesn't surrender power to him. Eventually she goes to war with her son over who should rule the kingdom. And even when she's defeated, she's not really excluded from politics. She just has to take a step back. She was a very powerful woman with a very magnetic personality who had been very clever in building up serious alliances in her kingdom.

article-image

What’s the legacy of the Crusader States and the women who ruled them?

It's a hard question because the Crusades were very bad. There's mass genocide and it's proto-colonialism. It's a very thorny topic. So, I mean, a hugely negative legacy, to be honest with you.

When it comes to the women of the Crusader States, I think Melisende's rule did influence the roles women play in medieval Europe. They saw women commanding in the Middle East, and that strengthened the case for women to have more power back home.

I also think it's very important to know that women were wielding power on both sides, both Christian and Islamic. That's often not what people expect. People often think of medieval princesses sitting alone in towers. If they think of Islamic princesses, they think of women in harems not able to go outside or be educated. But we know Saladin’s wife [Ismat ad-Din Khatun, who in 1176 married Saladin, the Muslim sultan who took back Jerusalem from the crusaders in 1187] is writing letters to him on an almost daily basis and was commanding negotiations and sieges. The myths aren't always true.

article-image

Why have these women been overlooked?

Because the chronicles are written by men. And they're pretty much always written by churchmen as well and obviously, in medieval times, churchmen aren't having a lot of experiences and interactions with women. So there's just a lot of discomfort about including the deeds of women in the chronicles because they're not considered to have the same power and importance as men. The chronicles often attribute things done by women to men.

And then that, in turn, influences how much modern historians write about women because if there's less source material, it's much harder to focus on them because there's less evidence for what they did. So the project of my book really was going through and collecting all the evidence for the deeds of these women and trying to string it together in a compelling and accurate way.

19 Jan 05:20

De zaak Horion, de Cafémoorden ... : tv-reeks ‘De Kroongetuigen’ vanaf vandaag ook als podcast te beluisteren

by Fien Tondeleir
‘Het Laatste Nieuws’ en ‘VTM Nieuws’ verrassen met een misdaadpodcast, ‘De kroongetuigen’, een bewerking van de populaire documentairereeks op tv. En dat is meteen een primeur voor Vlaanderen. “De verhalen zijn nog beklijvender als je ze enkel hoort.”
12 Nov 01:33

Diamond hauled from deep inside Earth holds never-before-seen mineral

Researchers have discovered a new mineral from deep within Earth's mantle; it was trapped inside a diamond and brought to the surface.
09 Sep 17:09

How do cats get their stripes?

Several key genes dictate whether a house cat will have stripes, spots or neither.
17 Aug 03:09

Don't abandon Afghans who helped UK, Labour warns government

The party calls for safe asylum routes for interpreters and others who worked with British forces.
14 Aug 21:34

LIVE. Hazard toont zich gretig, maar Real blijft voorlopig wachten op eerste échte kans

by Redactie
• 1ste speeldag • Aftrap: 22u • Estadio de Mendizorroza • Eden Hazard start, net als Thibaut Courtois
02 Feb 14:31

Socially Awkward Situations And Funny Random Cartoons by RandoWis

by Dmitry

The man behind these comics is RandoWis from Singapore. He bases all of his characters on real people and gathers the inspiration for the comics from his daily life and funny situations. Take a look at some of his best comics below.

“Thank you for watching my art and comics, I’m really grateful for that! If you like them comics, then stick around and tell your friends to share the love!”

More: RandoWis, Facebook, DeviantArt, Patreon h/t: sadanduseless

















































27 Nov 09:21

Ceo: Huawei verkoopt Honor-merk om banen te redden

by Arnoud Wokke
Huawei verkoopt zijn Honor-merk om de banen van medewerkers te redden, zo is op te maken uit een afscheidstoespraak van de ceo van Huawei. Het gaat in totaal om 'miljoenen' mensen die een baan hebben die afhankelijk is van het succes van het dochtermerk, aldus de ceo.
18 Nov 09:39

Review: AndaSeat Fnatic Edition treats your butt like a pro gamer's

by Loz Blain

You're damn right I cleaned up my desk for this shot. The AndaSeat Fnatic Edition Premium Gaming Chair makes you feel like you're sitting in a supercar in your office

Athlete endorsements are meaningful to people buying sporting goods. Want some basketball shoes? Check out what Steph Curry's wearing, if those kicks can keep his wonky, hundred-million-dollar ankles safe, chances are they'll work for you too. Tiger Woods ain't hackin' round the fairways with no crappy golf clubs, and you can probably trust Roger Federer to pick a half-decent tennis racquet.

Continue Reading

Category: Around The Home, Lifestyle

Tags: Seat, Reviews, Gaming, Chair, Ergonomic, Comfort

09 Jun 10:58

Trump’s Mideast plan is poised to fall apart

by Zev Chafets
Israeli Prime Minister Benjamin Netanyahu looks set to take the parts of the plan he likes and leave the rest.
28 Dec 11:22

Taliban graven tunnel naar legerbasis en doden tien militairen

Bij een aanval van de taliban op een militaire basis in Afghanistan zijn tien soldaten om het leven gekomen. Volgens een legerwoordvoerder groeven de aanvallers een tunnel tot onder de militaire basis, waarna ze hun explosieven tot ontploffing brachten.
05 Dec 20:00

Anthony Joshua v Andy Ruiz II: 'I'm punching like a horse kick'

Anthony Joshua says he is "punching like a horse kicking backwards" and holds "no fear" going into his highly anticipated rematch with Andy Ruiz Jr.
19 Mar 12:34

Strijd nog niet gestreden: “Nog duizenden IS-strijders verschansen zich in laatste bolwerk”

Iets minder dan een maand geleden kondigde de door Koerden aangevoerde troepen de eindstrijd tegen Islamitische Staat (IS) aan. Die eindstrijd...
05 Aug 00:59

Rhino rams car in Mexico safari park

A visitors' vehicle became the butt of a rhino's aggression at the park in Puebla, Mexico.
16 May 18:26

Dominic Thiem sneuvelt meteen in Rome

Dominic Thiem (ATP 8) is uitgeschakeld bij zijn eerste optreden op het Masters 1.000-toernooi in de Italiaanse hoofdstad Rome. Het Oostenrijkse...
28 Sep 10:43

Mechelen onthoudt zich bij Eandis-stemming

De Mechelse gemeenteraad heeft dinsdag beslist zich volgende week maandag tijdens de algemene vergadering van Eandis te onthouden bij de stemming over de deal met State Grid Europe Limited. Een aantal gemeenten uit de rand ten zuiden van Antwerpen, met onder meer Mortsel, Edegem en Hove, stemden tegen de deal.

28 Sep 10:40

Conny, Kurt en Sonja gaan lekker intiem

by redactie
Kleine medekijkers, doe snel je ogen toe! Wie Kurt Rogiers vraagt om enkele sensuele scenes uit een soap na te spelen krijgt dit...
28 Sep 10:40

Aleppo residents struggle to avoid starvation

In one part of rebel-held Aleppo, nearly all families have left - the ones who remain face starvation.