JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
<script>
//******************* Ignore this code **************************
var counter = 0;
var taskId = null;
function fakeApiValidator(state) {
if (window.taskId === 1) {
return fakeApiValidateCartItemsStateTask1(state);
}
if (window.taskId === 2) {
return fakeApiValidateCartItemsStateTask2(state);
}
if (window.taskId === 3) {
return fakeApiValidateCartItemsStateTask3(state);
}
}
function fakeApiValidateCartItemsStateTask1(state) {
return new Promise((resolve) => {
state.length === 1 && state.includes('Derila') ?
setTimeout(() => resolve(state), 100) :
resolve(state);
});
}
function fakeApiValidateCartItemsStateTask2(state) {
return new Promise((resolve, reject) => {
window.counter++;
if (window.counter < 3) {
reject('Network outage!');
}
resolve(state);
});
}
function fakeApiValidateCartItemsStateTask3(state) {
return new Promise((resolve, reject) => {
window.counter++;
setTimeout(() => resolve(state), 1000 * window.counter);
});
}
//***************************************************************
Alpine.data('component', function() {
return {
cartItemsState: [],
loader: null,
task: null,
taskStarted: null,
addCartItem(name) {
this.addCartItemQueue = this.addCartItemQueue.then(() => {
const newState = this.cartItemsState.concat(name);
return fakeApiValidator(newState)
.catch(() => fakeApiValidator(newState))
.catch(() => fakeApiValidator(newState))
.catch(() => [])
.then(response => this.cartItemsState = response)
});
return this.addCartItemQueue;
},
// For the first task you only need to edit addCartItem method. Task 1 simulates 3 addCartItem events that happen in a very short...
CSS
body {
background: white;
padding: 20px;
font-family: Arial, sans-serif;
background-color: #f0f0f0;
padding: 20px;
}
.component-container {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
padding: 20px;
max-width: 400px;
margin: 0 auto;
}
.component-container button {
background-color: #007bff;
color: #fff;
padding: 10px;
border-radius: 4px;
text-align: center;
cursor: pointer;
transition: background-color 0.3s;
margin-right: 10px;
}
.component-container p {
font-size: 1.2em;
margin-bottom: 10px;
font-weight: bold;
}
.component-container ul {
list-style-type: none;
padding: 0;
}
.component-container li {
padding: 8px;
border-bottom: 1px solid #ddd;
}
.component-container li:last-child {
border-bottom: none;
}
.run-test {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.run-test .alert {
background-color: #f0f0f0 !important;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
text-align: center;
}
.run-test .alert p {
margin: 0;
color: #c00;
font-size: 12px;
}
.run-test .alert p span {
font-weight: normal;
color: black;
}
.run-test .success {
background-color: #9FE582 !important;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
text-align: center;
}
.run-test .success p {
margin: 0;
color: #0B5800;
font-size: 12px;
}
.run-test .success p span {
font-weight: normal;
color: black;
}
.loader {
border: 4px solid #f3f3f3; /* Light grey */
border-top: 4px solid #3498db; /* Blue */
border-radius: 50%;
width: 20px;
height: 20px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}