我在 azure 中创建了一个 Web 应用程序,并尝试使用 MSAL React 库配置 sso。但是,当单击 sso 按钮时,它不会重定向到 /home 页面,而且令牌生成也不会发生。
Authconfig.js
export const msalConfig = {auth: {clientId: "client-id",authority: "\`\`https://login.microsoftonline.com/tenant-id\`\`",redirectUri: "\`\`http://localhost:5173/home\`\`",},cache: {cacheLocation: "sessionStorage", issuesstoreAuthStateInCookie: false,},};
main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { PricingComponent } from './PricingComponent.jsx';
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
import { AboutComponent } from './AboutComponent.jsx';
import { AdminComponent } from './AdminComponent.jsx';
import { BuildComponent } from './BuildComponent.jsx';
import { ContactusComponent } from './ContactusComponent.jsx';
import { FeedbackComponent } from './FeedbackComponent.jsx';
import { LandingPageComponent } from './LandingPageComponent.jsx';
import { LoginPageComponent } from './LoginPageComponent.jsx';
import { ReleasenotesComponent } from './ReleasenotesComponent.jsx';
import { UserHomeComponent } from './UserHomeComponent.jsx';
import { UserProfileComponent } from './UserProfileComponent.jsx';
import './index.css';
import { ProtectedRoutes } from './utils/ProtectedRoutes.jsx';
const router = createBrowserRouter([
{
path:'/',
element: <LandingPageComponent />,
errorElement: <div>404 not found</div>,
children : [
{
path: '',
element: <AboutComponent />,
},
{
path: 'pricing',
element: <PricingComponent />,
},
{
path: 'releasenotes',
element: <ReleasenotesComponent /> ,
},
{
path: 'contactus',
element: <ContactusComponent />,
},
{
path: 'signin',
element: <LoginPageComponent />,
},
{
element: <ProtectedRoutes />,
children: [
{
path: 'home',
element: <UserHomeComponent />,
},
{
path: 'build',
element: <BuildComponent />,
},
{
path: 'admin',
element: <AdminComponent /> ,
},
{
path: 'userprofile',
element: <UserProfileComponent /> ,
},
{
path: 'feedback',
element: <FeedbackComponent />,
},
],
},
],
},
]);
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>,
)
登录页面组件.jsx
import React, { useContext } from "react";
import { useNavigate } from 'react-router-dom';
import loginimage from './assets/loginimage.svg';
import { useMsal } from "@azure/msal-react";
import { loginRequest } from './Authconfig';
export const LoginPageComponent = () => {
const { instance } = useMsal();
const handleLogin = () => {
instance.loginRedirect(loginRequest);
};
return (
<div className="bg-white">
<div className="grid grid-cols-2 justify-center items-center m-20">
<div>
<img
loading="lazy"
src={loginimage}
className="self-end max-w-full aspect-square w-[540px]"
/>
</div>
<div className="flex flex-col ml-5">
<div className="flex flex-col p-10 mt-5 w-fit items-center justify-center rounded-lg shadow-sm bg-sky-600 bg-opacity-20 text-zinc-800 max-md:px-5 max-md:mt-10">
<div className="text-4xl font-black text-sky-600">
</div>
<button
onClick={handleLogin}
className="w-96 px-8 py-3 ml-6 rounded-lg bg-indigo-500 text-gray-100 border border-gray-200 placeholder-gray-500 text-sm tracking-wide font-semibold hover:bg-indigo-700 transition-all duration-300 ease-in-out flex items-center justify-center focus:shadow-outline focus:outline-none mt-5"
>
<svg className="w-6 h-6 -ml-2" fill="none" stroke="currentColor">
<path d="M16 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" />
<circle cx="8.5" cy="7" r="4" />
<path d="M20 8v6M23 11h-6" />
</svg>
<span className="ml-3">Login With SSO</span>
</button>
</div>
</div>
</div>
</div>
);
}
UserHomeComponent.jsx
import React, { PureComponent, useContext, useEffect, useState } from 'react';
import { AiOutlineExclamationCircle } from "react-icons/ai";
import { CiCircleRemove } from "react-icons/ci";
import { FaRegCircleCheck } from "react-icons/fa6";
import { PiClockCountdown } from "react-icons/pi";
import { Area, AreaChart, Bar, BarChart, Cell, Label, Pie, PieChart, Rectangle, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
import bannerlogo from './assets/buildnowlogo.svg';
import { AuthContext } from './utils/AuthContext';
export const UserHomeComponent = () => {
const [orgdata, setOrgData] = useState([]);
const [responsedata,setResponsedata] = useState([]);
const [responsedata2,setResponsedata2] = useState([]);
const [responsedata3,setResponsedata3] = useState([]);
const [changefailrate,setChangeFailRate] = useState([]);
const [failurejobs,setFailurejobs] =useState([]);
const [successjobs,setSuccessjobs] =useState([]);
const [unstablejobs,setUnstablejobs] =useState([]);
const [avgleadtime,setAvgleadtime] =useState([]);
const [searchTerm, setSearchTerm] = useState('');
const [queryDate, setQueryDate] = useState('30');
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
const {user} = useContext(AuthContext);
const [postdata,setPostData] = useState({orgname: "", days: "" , projectbasename:""});
const [ischange, setIsChange] = useState(false);
const [visibleTooltip, setVisibleTooltip] = useState(null);
const baseUrl = import.meta.env.VITE_REACT_APP_BACKEND_BASE_URL;
const RADIAN = Math.PI / 180;
const cx = 120;
const cy = 100;
const iR = 60;
const oR = 100;
const percentage = changefailrate.Percentage
const color = '#d0d000';
const isAdm = user.isServiceAdmin
const userid=user.userinfo.sAMAccountName
const memberrole=user.memberRoles
const handleSelectionChange = (e) => {
const [ofr, spr] = e.target.value.split(' ');
setPostData({orgname:ofr,days:queryDate,projectbasename:spr});
setIsChange(true);
};
const fetchOrgData = async () => {
const userdetail = {
username: userid,
isServiceAdmin: isAdm,
memberRoles:memberrole
};
const response= await fetch(`${baseUrl}/buildNowJenkins/getMultiBranch`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userdetail),
});
const result =await response.json();
setOrgData(result);
setPostData({orgname:result.multiBranchInfo[0].OrgFolder,days:queryDate,projectbasename:result.multiBranchInfo[0].MultiBranchProject});
setIsChange(true);
};
const handledateChange = (e) => {
const dat = e.target.value;
setQueryDate(dat);
postdata.days = dat
setIsChange(true);
};
const filteredList = (orgdata.multiBranchInfo || []).filter((item) =>
`${item.OrgFolder} ${item.MultiBranchProject}`
.toLowerCase()
.includes(searchTerm.toLowerCase())
);
useEffect(() => {
fetchOrgData();
}
, []);
const handleSearchChange = (e) => {
setSearchTerm(e.target.value);
};
const fetchfailurejobs =async() => {
setIsLoading(true);
try {
const response = await fetch(`${baseUrl}/buildNowAdm/buildnowdorametrics/projectfailurejobs/`,{
method:'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postdata),
});
const responsedata = await response.json();
setFailurejobs(responsedata)
} catch (error) {
console.error('Error fetching data:', error);
} finally{
setIsLoading(false);
}
};
const fetchsuccessjobs =async() => {
setIsLoading(true);
try {
const response = await fetch(`${baseUrl}/buildNowAdm/buildnowdorametrics/projectsuccessjobs/`,{
method:'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postdata),
});
const responsedata = await response.json();
setSuccessjobs(responsedata)
} catch (error) {
console.error('Error fetching data:', error);
} finally{
setIsLoading(false);
}
};
const fetchunstablejobs =async() => {
setIsLoading(true);
try {
const response = await fetch(`${baseUrl}/buildNowAdm/buildnowdorametrics/projectunstablejobs/`,{
method:'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postdata),
});
const responsedata = await response.json();
setUnstablejobs(responsedata)
} catch (error) {
console.error('Error fetching data:', error);
} finally{
setIsLoading(false);
}
};
const fetchaverageleadtime =async() => {
setIsLoading(true);
try {
const response = await fetch(`${baseUrl}/buildNowAdm/buildnowdorametrics/projectaverageleadtime/`,{
method:'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postdata),
});
const responsedata = await response.json();
setAvgleadtime(responsedata)
} catch (error) {
console.error('Error fetching data:', error);
} finally{
setIsLoading(false);
}
};
const projectleadtime = async () => {
setIsLoading(true);
try {
const response = await fetch(`${baseUrl}/buildNowAdm/buildnowdorametrics/projectleadtime/`,{
method:'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postdata),
});
const responsedata = await response.json();
setResponsedata2(responsedata)
} catch (error) {
console.error('Error fetching data:', error);
} finally{
setIsLoading(false);
}
};
const projectdeploymentfrequencydaywise = async () => {
setIsLoading(true);
try {
const response = await fetch(`${baseUrl}/buildNowAdm/buildnowdorametrics/projectdeploymentfrequencydaywise/`,{
method:'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postdata),
});
const responsedata = await response.json();
setResponsedata(responsedata)
} catch (error) {
console.error('Error fetching data:', error);
} finally{
setIsLoading(false);
}
};
const projectdeployment = async() => {
setIsLoading(true);
try {
const response = await fetch(`${baseUrl}/buildNowAdm/buildnowdorametrics/projectdeployment/`,{
method:'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postdata),
});
const responsedata = await response.json();
setResponsedata3(responsedata)
} catch (error) {
console.error('Error fetching data:', error);
} finally{
setIsLoading(false);
}
};
const changefailurerate = async() => {
try {
const response = await fetch(`${baseUrl}/buildNowAdm/buildnowadminmetrcs/changefailurerate/`,{
method:'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postdata),
});
const rd = await response.json();
setChangeFailRate(rd)
} catch (error) {
console.error('Error fetching data:', error);
}
};
const LoadingComponent = () => {
return (
<div class="border w-96 h-44 border-gray-100 ">
<div class="animate-pulse flex">
<div class=" flex space-x-4 items-end relative ml-20">
<div class="w-4 bg-slate-200 rounded h-16"></div>
<div class="w-4 bg-slate-200 rounded h-32"></div>
<div class="w-4 bg-slate-200 rounded h-24"></div>
<div class="w-4 bg-slate-200 rounded h-16"></div>
<div class="w-4 bg-slate-200 rounded h-32"></div>
<div class="w-4 bg-slate-200 rounded h-20"></div>
<div class="w-4 bg-slate-200 rounded h-8"></div>
<div class="w-4 bg-slate-200 rounded h-32"></div>
<div class="w-4 bg-slate-200 rounded h-20"></div>
<div class="w-4 bg-slate-200 rounded h-24"></div>
<div class="w-4 bg-slate-200 rounded h-32"></div>
<div class="w-4 bg-slate-200 rounded h-36"></div>
<div class="w-4 bg-slate-200 rounded h-20"></div>
<div class="w-4 bg-slate-200 rounded h-32"></div>
<div class="w-4 bg-slate-200 rounded h-16"></div>
<div class="w-4 bg-slate-200 rounded h-32"></div>
<div class="w-4 bg-slate-200 rounded h-24"></div>
<div class="w-4 bg-slate-200 rounded h-16"></div>
<div class="w-4 bg-slate-200 rounded h-32"></div>
<div class="w-4 bg-slate-200 rounded h-20"></div>
<div class="w-4 bg-slate-200 rounded h-8"></div>
<div class="w-4 bg-slate-200 rounded h-32"></div>
<div class="w-4 bg-slate-200 rounded h-20"></div>
<div class="w-4 bg-slate-200 rounded h-24"></div>
<div class="w-4 bg-slate-200 rounded h-32"></div>
<div class="w-4 bg-slate-200 rounded h-36"></div>
<div class="w-4 bg-slate-200 rounded h-20"></div>
<div class="w-4 bg-slate-200 rounded h-32"></div>
</div>
</div>
</div>
)
};
class CustomizedLabel extends PureComponent {
render() {
const { x, y, value } = this.props;
return (
<text x={x} y={y} dy={-4} fontSize={10} textAnchor="middle">
{value}
</text>
);
}
}
const needle = (percentage, cx, cy, iR, oR, color) => {
const ang = 180.0 * (1 - percentage / 100); // calculate angle based on percentage
const length = (iR + 2 * oR) / 3;
const sin = Math.sin(-RADIAN * ang);
const cos = Math.cos(-RADIAN * ang);
const r = 5;
const x0 = cx + 5;
const y0 = cy + 5;
const xba = x0 + r * sin;
const yba = y0 - r * cos;
const xbb = x0 - r * sin;
const ybb = y0 + r * cos;
const xp = x0 + length * cos;
const yp = y0 + length * sin;
return [
<circle key="circle" cx={x0} cy={y0} r={r} fill={color} stroke="none" />,
<path key="path" d={`M${xba} ${yba}L${xbb} ${ybb} L${xp} ${yp} L${xba} ${yba}`} stroke="none" fill={color} />,
];
};
if (ischange===true)
{
console.log(postdata);
changefailurerate();
projectdeployment();
fetchsuccessjobs();
fetchfailurejobs();
fetchunstablejobs();
fetchaverageleadtime();
projectleadtime();
projectdeploymentfrequencydaywise();
setIsChange(false);
};
return (
<>
{
user.memberRoles.length === 0 ? (
<div className=' grid justify-center items-center h-[90%] bg-slate-100 '>
<div className=' shadow-md p-10 bg-slate-200 m-20'>
<div className='flex items-center justify-center mb-10'>
<img src={bannerlogo} />
<h5 className='text-6xl text-center font-extrabold text-sky-700 ml-5'>buildNow</h5>
</div>
<h5 className='text-3xl text-center text-slate-400 mb-5'>oops!...</h5>
<p className='text-center text-slate-700 text-2xl font-normal'>
Your <span className='font-bold'> projects haven't been </span> migrated yet or your <span className='font-bold'>users haven't been assigned to the</span> IdM2BCD <span className='font-bold'>role</span>. <br/>
Contact the <span className='font-bold'>buildNow team</span> for assistance.
</p>
</div>
</div> ) : (
<>
<div className="justify-center items-center text-center p-2 mt-5 w-full text-2xl font-bold leading-9 text-sky-600 rounded-xl border border-black border-solid bg-zinc-900 max-md:px-5 max-md:max-w-full">
DORA+ METRICS
</div>
<div className="grid grid-flow-col text-sky-800 font-bold justify-center items-center gap-10 mt-2 p-1">
<div>
<input
type="text"
placeholder="Search Project..."
value={searchTerm}
onChange={handleSearchChange}
className='w-96'
/>
<div>
<select className='w-96'onChange={handleSelectionChange} value={`${postdata.orgname} ${postdata.projectbasename}`} >
{filteredList.map((item) => (
<option key={item.MultiBranchProject} value={`${item.OrgFolder} ${item.MultiBranchProject}`}>
{item.OrgFolder} {item.MultiBranchProject}
</option>
))}
</select>
</div>
</div>
<div>
<select size={1} defaultValue="30" onChange={handledateChange} value={postdata.days}
className="block w-80 p-3 mt-1 border-b-2 border-gray-400 rounded-sm shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<option value="7" id="daysDropdown">7 days</option>
<option value="14" id="daysDropdown">14 days</option>
<option value="30" id="daysDropdown">30 days</option>
</select>
</div>
</div>
<div className=' text-center'>
{postdata.orgname && postdata.projectbasename && (
<div >
<p><span className=' font-semibold ' >Org:</span> {postdata.orgname}, <span className=' font-semibold' > Project:</span> {postdata.projectbasename}</p>
</div>
)}
</div>
<div className=' grid grid-cols-12'>
<div className='col-span-4 sm:gap-2 gap-4 mt-5 justify-center ml-5'>
<div className=' flex flex-grow gap-2 md:gap-4 '>
<div className="items-center justify-center bg-gray-50 rounded-xl shadow-sm mb-2">
<div className='flex flex-auto items-center justify-center'>
<h5 className="text-lg font-semibold text-gray-900 pe-1">Change Failure Rate</h5>
<div className='relative flex items'>
<AiOutlineExclamationCircle
size={10}
onMouseEnter={() => setVisibleTooltip('1')}
onMouseLeave={() => setVisibleTooltip(null)}
className="cursor-pointer " />
{visibleTooltip === '1' && (
<div className="absolute bottom-full mb-2 w-96 p-2 bg-slate-50 text-sm rounded-md shadow-lg">
Change failure rate represents the percentage of deployments that result in failures in the production environment.
</div>
)}</div>
</div>
<div className='w-64 h-32' >
<ResponsiveContainer >
<PieChart >
<Pie
dataKey="value"
startAngle={180}
endAngle={0}
data={[{ value: 100, color: '#ff1a1a' }]} // single slice to fill the semi-circle
cx={cx}
cy={cy}
innerRadius={iR}
outerRadius={oR}
fill="#8884d8"
stroke="none"
>
<Label value={`${percentage}%`}/>
<Cell fill="#ff4d4d" /> {/* background color */}
</Pie>
{needle(percentage, cx, cy, iR, oR, color)}
</PieChart>
</ResponsiveContainer>
</div>
</div>
<div className="grid justify-center w-40 h-40 bg-slate-50 rounded-xl shadow-sm ">
<div className="flex flex-col items-center pb-10 mt-2">
<div className='flex flex-auto justify-center'>
<p className="mb-5 text-sm font-semibold font-sans pe-1">Overall Deployment</p>
<div className='relative flex mt-1'>
<AiOutlineExclamationCircle
size={10}
onMouseEnter={() => setVisibleTooltip('2')}
onMouseLeave={() => setVisibleTooltip(null)}
className="cursor-pointer" />
{visibleTooltip === '2' && (
<div className="absolute bottom-full mb-2 w-96 p-2 bg-slate-50 text-sm rounded-md shadow-lg">
The total number of deployments carried out during the given time frame.
</div>
)}</div>
</div>
<h5 className="mt-3 text-4xl font-medium text-gray-900">{responsedata3.count}</h5>
<PiClockCountdown size={50} color="blue" />
</div>
</div>
</div>
<div className='flex flex-auto gap-2 md:gap-4 ml-2 mb-5 md:mt-5 lg:mt-10'>
<div className="grid justify-center w-40 h-40 bg-slate-50 rounded-xl shadow-sm ">
<div className="flex flex-col items-center pb-10 mt-5">
<div className='flex flex-auto justify-center'>
<p className="mb-2 text-md font-semibold font-sans pe-1 ">Success Jobs</p>
<div className='relative flex mt-1'>
<AiOutlineExclamationCircle
size={10}
onMouseEnter={() => setVisibleTooltip('3')}
onMouseLeave={() => setVisibleTooltip(null)}
className="cursor-pointer" />
{visibleTooltip === '3' && (
<div className="absolute bottom-full mb-2 w-96 p-2 bg-slate-50 text-sm rounded-md shadow-lg">
The total number of builds that were completed successfully during the specified time period.
</div>
)}</div>
</div>
<h5 className="mb-2 text-4xl font-medium text-gray-900">{successjobs.count}</h5>
<FaRegCircleCheck size={50} color="#00b300" />
</div>
</div>
<div className="grid justify-center w-40 h-40 bg-slate-50 rounded-xl shadow-sm ">
<div className="flex flex-col items-center pb-10 mt-5">
<div className='flex flex-auto justify-center'>
<p className="mb-2 text-md font-semibold font-sans pe-1">Failure Jobs</p>
<div className='relative flex mt-1'>
<AiOutlineExclamationCircle
size={10}
onMouseEnter={() => setVisibleTooltip('4')}
onMouseLeave={() => setVisibleTooltip(null)}
className="cursor-pointer" />
{visibleTooltip === '4' && (
<div className="absolute left-full mb-2 w-96 p-2 bg-slate-50 text-sm rounded-md shadow-lg">
The total number of builds that failed during the specified time period
</div>
)}</div>
</div>
<h5 className="mb-2 text-4xl font-medium text-gray-900">{failurejobs.count}</h5>
<CiCircleRemove size={50} color="#ff4d4d" />
</div>
</div>
<div className="grid justify-center w-40 h-40 bg-slate-50 rounded-xl shadow-sm ">
<div className="flex flex-col items-center pb-10 mt-5">
<div className='flex flex-auto justify-center'>
<p className="mb-2 text-md font-semibold font-sans pe-1">Unstable Jobs</p>
<div className='relative flex mt-1'>
<AiOutlineExclamationCircle
size={10}
onMouseEnter={() => setVisibleTooltip('5')}
onMouseLeave={() => setVisibleTooltip(null)}
className="cursor-pointer" />
{visibleTooltip === '5' && (
<div className="absolute left-full mb-2 w-64 p-2 bg-slate-50 text-sm rounded-md shadow-lg">
The total number of Unstable builds carried out during the given time frame.
</div>
)}</div>
</div>
<h5 className="mb-2 text-4xl font-medium text-gray-900">{unstablejobs.count}</h5>
<AiOutlineExclamationCircle size={50} color="#ff8000" />
</div>
</div>
</div>
</div>
<div className="col-span-8 mt-5 gap-4 m-2 justify-center ">
<div className=" items-center justify-center bg-gray-50 rounded-xl shadow-sm mb-1 md:mb-5 lg:mb-15 ">
<div class="flex justify-between items-center">
{ avgleadtime.AverageLeadTime && (
<div class="text-left text-xs">
Avg Lead Time<span className='text-2xl font-medium'>({avgleadtime.AverageLeadTime})</span>
</div>)}
<div class="mx-auto text-xl font-semibold relative flex">
Lead Time
<AiOutlineExclamationCircle
size={10}
onMouseEnter={() => setVisibleTooltip('6')}
onMouseLeave={() => setVisibleTooltip(null)}
className=" cursor-pointer ml-1 mt-2" />
{visibleTooltip === '6' && (
<div className="absolute bottom-full mb-2 w-96 p-2 bg-slate-50 text-sm font-normal rounded-md shadow-lg">
Lead time for changes is the amount of time it takes a code change to get into production.
This metric enables businesses to quantify the speed of code delivery to customers or stakeholders.
</div>
)}
</div>
</div>
{isLoading ? <LoadingComponent /> : responsedata.length === 0 ? (
<div className="h-44 text-center items-center justify-center pt-10 text-gray-300 font-extrabold text-2xl">Metrics not available for selected time period</div>
) : (
<div className='w-full h-full'>
<ResponsiveContainer width="100%" height={200}>
<BarChart
data={responsedata2}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
>
<XAxis dataKey="time">
<Label value="Date" offset={-5} position="insideBottom" />
</XAxis>
<YAxis>
<Label value="Lead Time(mins)" angle={-90} position="insideLeft" style={{ textAnchor: 'middle' }} />
</YAxis>
<Tooltip dataKey="time" />
<Bar dataKey="last" fill="#3366ff" activeBar={<Rectangle fill="purple" />} />
</BarChart>
</ResponsiveContainer>
</div>)
}
</div>
<div className=" items-center justify-center bg-gray-50 rounded-xl shadow-sm ">
<div className='flex flex-auto justify-center'>
<h5 className="text-xl font-semibold leading-none text-center justify-center text-gray-900 pe-1">Deployment Frequency Day Wise</h5>
<div className='relative flex mt-1'>
<AiOutlineExclamationCircle
size={10}
onMouseEnter={() => setVisibleTooltip('7')}
onMouseLeave={() => setVisibleTooltip(null)}
className=" cursor-pointer" />
{visibleTooltip === '7' && (
<div className="absolute bottom-full mb-2 w-96 p-2 bg-slate-50 text-sm rounded-md shadow-lg">
Day wise deployments carried out during the given time frame
</div>
)}</div>
</div>
{isLoading ? (<LoadingComponent />) : responsedata.length === 0 ? (
<div className="h-44 text-center items-center justify-center pt-20 text-gray-300 font-extrabold text-2xl">Metrics not available for selected time period</div>
) : (
<div>
<ResponsiveContainer width="100%" height={200}>
<AreaChart
data={responsedata}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
>
<XAxis dataKey = "time" >
<Label value="Date" offset={-5} position="insideBottom" />
</XAxis>
<YAxis>
<Label value="count" angle={-90} position="insideLeft" style={{ textAnchor: 'middle' }} />
</YAxis>
<Tooltip />
<Area dataKey="count" fill="#0040ff" activeDot={{ r: 3 }} />
</AreaChart>
</ResponsiveContainer>
</div>)
}
</div>
</div>
</div>
</>
)
}
</>
)
}
AuthContext.jsx
import React, { useState } from 'react';
const AuthContext = React.createContext();
const AuthProvider = ({ children }) => {
const [user, setUser] = useState(() => {
const storedUser = localStorage.getItem('user');
return storedUser ? JSON.parse(storedUser) : null;
});
const login = (userData) => {
setUser(userData);
localStorage.setItem('user', JSON.stringify(userData));
};
const logout = () => {
setUser(null);
localStorage.removeItem('user');
};
return (
<AuthContext.Provider value={{ user,setUser,login,logout }}>
{children}
</AuthContext.Provider>
);
};
export { AuthContext, AuthProvider };
ProtectedRoutes.jsx
import React, { useContext } from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { AuthContext } from './AuthContext';
export const ProtectedRoutes = () => {
const { account } = useContext(AuthContext);
return account ? <Outlet/> : <Navigate to="/signin" />;
};
登陆页面组件.jsx
import React from 'react';
import { Outlet } from 'react-router-dom';
import { Header } from './Header';
import { AuthProvider } from './utils/AuthContext';
export const LandingPageComponent = () => {
return (
<>
<AuthProvider>
<Header />
<Outlet />
</AuthProvider>
</>
)
}
单击 SSO 按钮后,需要验证用户并需要重定向到 Userhomecomponent.jsx 页面。
我已将
http://localhost:5173/home
配置为 azure 中的redirectUri。我还添加了范围 openid、电子邮件和个人资料。我还启用了访问令牌和 id 令牌
您可以使用 Uri 最佳实践来正确验证并重定向到 UserHomeComponent.jsx,文档建议您可以对重定向 Uri 使用 HTTPS 和 HTTP 架构。
注意:HTTP:HTTP 方案 (http://) 仅支持 localhost URI,并且仅应在活动的本地应用程序开发和测试期间使用。
该注释表明您的架构部分正确http://localhost:5173/home,因此,如果您使用的是 Windows,请尝试添加指向 localhost 的 IPv4(我们假设您的应用程序正在运行到 Web 或模拟器中)同一台机器),尝试获取与本地主机等效的 IPv4,这样生产服务器/开发服务器将正确重定向到 UserHome 组件。
通常,您可以使用 ipconfig 命令或类似命令来获取它,
更多信息请访问:https://learn.microsoft.com/en-us/entra/identity-platform/reply-url#prefer-127001-over-localhost